3.5 Datentypen für Zeichen
In diesem Abschnitt erfahren Sie etwas zu den Zeichentypen in C. Zwar werden Sie in diesem Buch vorwiegend mit dem Datentyp char arbeiten, trotzdem sollen Sie auch kurz erfahren, welche Möglichkeiten es gibt, um erweiterte bzw. umfangreichere Zeichensätze zu verwenden. Beachten Sie, dass die Verwendung von erweiterten Zeichensätzen (wie Unicode) jenseits von char kein triviales Thema mehr ist. Für Sie ist es daher zunächst einmal wichtig, dass Sie sich mit dem Datentyp char vertraut machen. Wenn Sie anfangen, internationale Programme zu schreiben, werden Sie sich auch intensiver mit dem Thema Unicode befassen müssen, aber dafür benötigen sie auf jeden Fall den aktuellen Unicode-Standard und separate Literatur. Wir werden jetzt also im weiteren Verlauf des Buches einfach annehmen, dass ein Zeichen von Typ char ein Byte umfasst.
3.5.1 Der Datentyp char
Der grundlegende Datentyp für Zeichen lautet char und belegt für gewöhnlich ein Byte Speicherplatz, was somit meistens (aber nicht immer) 28 = 256 Ausprägungen ermöglicht. Der Datentyp char ist zumindest groß genug, dass alle Zeichen des Basis-Ausführungszeichensatzes darin gespeichert werden können. Wird außerdem ein Zeichen in einem char gespeichert, dann ist es garantiert, dass der gespeicherte Wert der nicht negativen Kodierung im Zeichensatz entspricht. Ein Zeichensatz wiederum ist dazu da, einem Zeichen einen bestimmten Wert zuzuweisen, weil ein Rechner letztendlich nur Dualzahlen speichern kann. Dazu ein Beispiel:
char ch = 'A'; // Dezimal 65 im ASCII-Zeichensatz
Bei dem weit verbreiteten ASCII-Zeichensatz entspricht die Zeichenkonstante 'A' dem Dezimalwert 65. So wäre es beispielsweise auch möglich, stattdessen folgendes anzugeben:
char ch = 65; // Entspricht dem Zeichen 'A' (ASCII)
Das ist ohne Probleme möglich, weil char ja auch ein Integer-Typ ist. In der Praxis ist die Verwendung eines Dezimalwerts anstelle einer Zeichenkonstante allerdings nicht zu empfehlen, wenn Sie char als Zeichentyp und nicht als Integer-Typ verwenden wollen. Zum einen lässt sich nicht sofort erkennen, welches Zeichen hier dahintersteckt, und zum anderen schreibt der Standard nicht vor, welcher Zeichensatz verwendet werden soll. Zwar dürfte zu 99,9 % die ASCII-Zeichensatztabelle zum Einsatz kommen, aber wenn dann doch einmal ein anderer Zeichensatz verwendet wird, sind Sie mit der Zeichenkonstante immer auf der sicheren Seite. Hierzu folgt ein einfaches Beispiel:
00 // Kapitel3/char_beispiel.c
01 #include <stdio.h>
02 int main(void) {
03 char ch01 = 'A'; // bei ASCII 65
04 char ch02 = 66; // besser wäre 'B'
05 printf("Dezimal: %d; Zeichen: %c\n", ch01, ch01);
06 printf("Dezimal: %d; Zeichen: %c\n", ch02, ch02);
07 return 0;
08 }
Das Programm gibt bei der Ausführung Folgendes aus:
Dezimal: 65; Zeichen: A
Dezimal: 66; Zeichen: B
char ist auch ein Ganzzahldatentyp, mit dem Sie rechnen können. Aufgrund des kleineren Wertebereichs wird dieser Typ dazu jedoch relativ selten genutzt.
Vorzeichen bei char
Es hängt von der Compiler-Einstellung ab, ob char auch negative Zahlen aufnehmen kann – ob char also als signed char oder unsigned char implementiert ist. Dies ist u. a. dann wichtig zu wissen, wenn Sie char als Typ für Ganzzahlen verwenden wollen.
3.5.2 Der Datentyp wchar_t
Für die Zeichensätze mancher Sprachen wie etwa der chinesischen mit über tausend Zeichen ist der Datentyp char zu klein. Für die Darstellung beliebiger landesspezifischer Zeichensätze kann daher der Breitzeichentyp wchar_t (wide char = breite Zeichen) aus der Header-Datei stddef.h verwendet werden. Bei der Verwendung eines solchen Zeichens muss vor die einzelnen Hochkommata noch das Präfix L gestellt werden:
wchar_t ch = L'Z';
Entsprechend wird auch bei dem Formatzeichen für die Ausgabe oder Eingabe eines wchar_t ein l vor das c gesetzt (%lc):
wprintf("%lc", ch);
Auch die üblichen Funktionen, die mit Zeichenketten arbeiten, können Sie mit wchar_t nicht mehr verwenden, und Sie müssen stattdessen auf die entsprechenden w-Versionen (wie beispielsweise wprintf()) zurückgreifen.
Die Größe von wchar_t lässt sich nicht exakt beschreiben, meistens beträgt sie jedoch 2 oder 4 Bytes. Es lässt sich lediglich mit Sicherheit sagen, dass wchar_t mindestens so groß wie char und höchstens so groß wie long ist. wchar_t muss auf jeden Fall mindestens groß genug sein, um alle Werte des größten unterstützten Zeichensatzes aufnehmen zu können.
Zeichen und Zeichensatz
Egal welchen Zeichentyp Sie verwenden, Sie sollten sich immer vor Augen halten, dass char und wchar_t selbst keine Zeichen speichern, sondern letztendlich nur Ganzzahlen, die ihre Bedeutung erst mit dem auf dem Rechner befindlichen Zeichensatz erhalten. Dies gilt selbstverständlich auch für Ihren Quellcode und die in ihm enthaltenen Kommentare, die auf fremden Rechnern unter Umständen nicht lesbar sind (z. B. Umlaute oder französische Akzente).
3.5.3 Unicode-Unterstützung
Der Unicode-Standard definiert mit UTF-8, UTF-16 und UTF-32 drei Zeichenkodierungsformate. Jedes Format hat seine Vor- und Nachteile. Bisher haben Programmierer char verwendet, um UTF-8 zu nutzen, unsigned short oder wchar_t für UTF-16 und unsigned long oder wchar_t für UTF-32. Ab dem C11-Standard können Sie zwei Typen mit einer plattformunabhängigen Breite mit char16_t und char32_t für UTF-16 und UTF-32 aus der Header-Datei uchar.h nutzen.
#include <uchar.h>
char utf8ch = u8'Z'; // UTF-8
char16_t utf16ch = u'Z'; // UTF-16
char32_t utf32ch = U'Z'; // UTF-32
Für UTF-8 können Sie nach wie vor char verwenden. C11 hat außerdem die Präfixe u und U für UTF-16- bzw. UTF-32-Literale und das Präfix u8 für UTF‐8-Literale eingeführt. Auch Unicode-Konvertierungsfunktionen sind in uchar.h deklariert.