11.11 Strings/Zeichenketten (»char«-Array) 

Arrays vom Datentyp char werden Strings genannt. Ein String ist eine Kette von einzelnen char-Zeichen mit einer abschließenden 0 (was nicht mit dem Zeichen '0' gleichzusetzen ist). char-Arrays sind typischerweise eindimensional.
Viele Programmierer, die auf die Programmiersprache C stoßen, sind verwundert, dass es keinen eigenen Datentyp für einen String gibt. Für ein char-Array gelten nicht nur die Einschränkungen der herkömmlichen Arrays, sondern es existiert auch das Problem der maximalen Länge von Arrays. Diese scheinbare Unflexibilität kann später, wenn sie effektiv eingesetzt wird, sehr ressourcensparend und schnell sein. Belassen Sie es aber erst einmal in den nächsten Kapiteln bei den etwas eingeschränkten char-Arrays.
Mit dem char-Array können Zeichenfolgen dargestellt und verarbeitet werden. Damit können Sie Benutzerschnittstellen und Textdateien verarbeiten und erstellen.
Eine Form der Stringkonstante wurde schon öfter in diesem Buch verwendet:
printf("Ich bin die Stringkonstante");
Generell lässt sich Folgendes sagen: Alles, was sich zwischen zwei Hochkommata befindet, gilt als Stringkonstante.
Die Deklaration eines char-Arrays ist identisch mit der bisher bekannten Form der Array-Deklaration:
char string_array[100];
Im obigen Beispiel wird ein Array vom Datentyp char angelegt, das 100 einzelne Zeichen speichern kann. Dabei muss die Größe des Array-Feldes nicht mehr ausdrücklich mit angegeben werden:
const char hallo[] = { 'H', 'a', 'l', 'l', 'o', ' ', 'W', 'e', 'l', 't', '\n', '\0' };
Diese Schreibweise ist ebenfalls absolut korrekt, aber sehr umständlich. Daher können Sie ein char-Array auch anders, nämlich als einen String (dt. Zeichenkette), deklarieren:
const char hallo[] = { "Hallo Welt\n" };
Beide Variationen sind absolut gleichwertig. Abbildung 11.12 zeigt die rechnerinterne Darstellung des Strings.
Abbildung 11.12 Der String »Hallo Welt« ist ein einfaches »char«-Array.
Diese Zeichenkette benötigt zwölf Elemente (genauer: zwölf Bytes). Wenn Sie aber die Deklaration zu hallo[] genauer betrachten, werden Sie feststellen, dass hierfür eigentlich nur elf Zeichen erforderlich wären. Welche Bedeutung hat das zwölfte Zeichen? Bei einem String benötigen Sie immer ein Stringende-Zeichen, das das Ende eines Strings anzeigt. Das ist die Bedeutung des Zeichens '\0'. Das versehentliche Weglassen des Zeichens ist eine häufige Fehlerquelle, wenn beispielsweise Speicher für n Zeichen reserviert werden soll. Generell muss also bei einem Bedarf von n Zeichen immer für n+1 Zeichen Platz im Array reserviert werden. Hierzu ein kurzes Listing:
/* string1.c */ #include <stdio.h> #include <stdlib.h> char hello1[] = { "Hallo Welt\n" }; char output[] = { "Ich bin lesbar \0 Ich nicht mehr" }; char deznu[] = { "Mich siehst du 0 Mich und die Null auch" }; int main(void) { printf("%s",hello1); printf("%s\n",output); printf("%s\n",deznu); return EXIT_SUCCESS; }
Dieses Beispiel zeigt auch, wie Sie für die formatierte Ausgabe von Zeichenketten die Formatangabe %s verwenden können (s = String). Daher benötigt ein String ein Ende-Kennungszeichen. Bei dem String
char output[] = {"Ich bin lesbar \0 Ich nicht mehr"};
werden nur die Zeichen bis '\0' angezeigt. Der hintere Teil des Strings existiert nur im Arbeitsspeicher. Da zuvor das Zeichen für das Ende des Strings '\0' steht, wird dieser Teil nie auf dem Bildschirm ausgegeben. Im nächsten Beispiel
char deznu[] = {"Mich siehst du 0 Mich und die Null auch"};
wird der ganze String ausgegeben, weil das Zeichen '0' nicht gleichzusetzen ist mit dem Zeichen '\0'.
Es wurde bereits erwähnt, dass es auch möglich ist, auf die einzelnen Zeichen eines Strings zuzugreifen. Wenn Sie einen String beispielsweise mithilfe einer for-Schleife auf seine Länge hin überprüfen wollen, prüfen Sie lediglich auf das Zeichen '\0'. Hier ein Beispiel dazu:
/* string2.c */ #include <stdio.h> #include <stdlib.h> char hello1[] = { "Hallo Welt" }; char output[] = { "Ich bin lesbar \0 Ich nicht mehr" }; char deznu[] = { "Mich siehst du 0 Mich und die Null auch" }; int main(void) { int i; printf("%c", output[0]); /* I */ printf("%c'", hello1[9]); /* t */ printf("%c ", deznu[5]); /* s */ printf("%c", hello1[7]); /* Gibt das Zeichen 'e' aus */ printf("%c", output[12]); /* a */ printf("%c", deznu[5]); /* s */ deznu[1] = 'y'; /* aus 'i' wird 'y' */ printf("%c\n", deznu[1]); /* y */ for(i=0; hello1[i] != '\0'; i++); printf("Länge von '%s' = %d Zeichen\n", hello1, i); for(i=0; output[i] != '\0'; i++); printf("Länge von '%s' = %d Zeichen\n", output, i); for(i=0; deznu[i] != '\0'; i++); printf("Länge von '%s' = %d Zeichen\n", deznu, i); return EXIT_SUCCESS; }
Hier werden mit dem Feldindex einzelne Zeichen ausgegeben, genau wie bei den Arrays mit Zahlen. In diesem Fall wird der Text »It's easy« ausgegeben. Es ist natürlich auch möglich, den Inhalt zu verändern, etwa so wie in der Zeile
deznu[1] = 'y';
Anschließend wird mit
for(i=0; hello1[i] != '\0'; i++);
die Anzahl der Zeichen hochgezählt, die sich im String hello1[ ] ohne \0 befinden. Die Abbruchbedingung
hello1[i] != '\0';
ist so lange wahr, bis der Inhalt von hello1[i] == '\0' ist. In diesem Beispiel wäre das bei hello1[11]der Fall, da sich hier das Zeichen '\0' befindet. Beachten Sie hier, dass hinter der for-Schleife ein Semikolon steht. Es gibt in diesem Fall keinen Anweisungsblock zur for-Schleife. Hier wird die Variable i so lange hochgezählt, bis das Stringende-Zeichen '\0' erreicht wird. Gleiches gilt für die anderen beiden Strings.
11.11.1 Vom String zur Binärzahl 

Oben habe ich behauptet, dass es in C keine Datentypen gibt, die Zeichen darstellen können. Die Zeichen wurden mithilfe der ASCII-Code-Tabelle kodiert. Wie verhält sich dies jetzt mit der folgenden Stringkonstante?
char str[] = { "Hallo!\n" };
Wird dieser String in seine einzelnen Zeichen zerlegt, ergibt sich die Zeile:
/* gleichwertig zu "Hallo!\n" */ char str[] = { 'H', 'a', 'l', 'l', 'o', '!', '\n', '\0' };
Werden die einzelnen Zeichen jetzt anhand der ASCII-Code-Tabelle dekodiert, sieht der String schon anders aus:
char str[] = { 72, 97, 108, 108, 111, 33, 10, 0 };
Theoretisch könnten Sie den String auch so angeben und ausgeben lassen, wie das folgende Listing demonstriert:
/* string3.c */ #include <stdio.h> #include <stdlib.h> int main(void) { /* Hallo!\n ... */ char str[] = { 72, 97, 108, 108, 111, 33, 10, 0 }; printf("%s\n",str); return EXIT_SUCCESS; }
Wenn Sie jetzt noch die einzelnen Werte in Binärzahlen umrechnen, können Sie den String aus der Sicht des Computers betrachten:
0100100001100001011011000110110001101111001000010000101000000000
Diese Erklärung soll Ihnen nur zeigen, dass Zeichenketten nicht magischer sind als ganz normale Zahlen-Arrays und in gewisser Hinsicht auch solche sind. Sie wollen einen Beweis? Bitte sehr:
/* string4.c */ #include <stdio.h> #include <stdlib.h> int main(void) { int i; int str[] = { 72, 97, 108, 108, 111, 33, 10, 0 }; for(i = 0; i < sizeof(str) / sizeof(int); i++) printf("%c", str[i]); return EXIT_SUCCESS; }
Ein kleines Programm demonstriert im Folgenden den Umgang mit Strings. Das Programm durchläuft eine Zeichenkette und wandelt alle Stringfolgen "und" in Großbuchstaben um:
/* string5.c */ #include <stdio.h> #include <stdlib.h> char undbig[] = { "Hund und Katze sind nicht ohne " "Grund des Menschen beste Freunde\n" }; int main(void) { int i; for(i=0; undbig[i] != '\0'; i++) { if(undbig[i-1]==' '&& (undbig[i]=='u' ||undbig[i]=='U')) { if(undbig[i+1]=='n'&&undbig[i+2]=='d'&&undbig[i+3]==' '){ undbig[i] = 'U'; /* n in Großbuchstaben konvertieren (N) */ undbig[i+1] -= 32; /* d in Großbuchstaben konvertieren (D) */ undbig[i+2] -= 32; } } } printf("%s", undbig); return EXIT_SUCCESS; }
Zu Beginn des Programms sehen Sie, wie Sie eine Stringkonstante über mehrere Zeilen schreiben können:
char array[] = { "Eine Zeichenkette über" "2 Zeilen\n" }; /* Alternative */ char array[] = { "Eine Zeichenkette über \ 2 Zeilen"};
Beide Schreibweisen erfüllen den gleichen Zweck. Fahren wir mit der ersten for-Schleife des Programms fort:
for(i=0; undbig[i] != '\0'; i++)
Hier wird der String zeichenweise durchlaufen, bis das Stringende-Zeichen '\0' gefunden wird. Bei der nächsten Anweisung
if(undbig[i-1] == ' ' && (undbig[i]=='u' || undbig[i]=='U'))
wird überprüft, ob das Zeichen, bei dem sich der Feldindex gerade befindet, ein kleines 'u' oder ein großes 'U' und das Zeichen davor ein Whitespace-Zeichen (Leerzeichen) ist. Falls nicht, wird i in der for-Schleife um den Wert 1 inkrementiert. Wird ein 'u' oder 'U' gefunden, folgt die Überprüfung
if(undbig[i+1]=='n' && undbig[i+2]=='d' && undbig[i+3]==' ')
daraufhin, ob die nächsten beiden Zeichen 'n' und 'd' sind und ob sich dahinter ebenfalls ein Whitespace-Zeichen befindet. Falls dies ebenso zutrifft, wurde eine Zeichenfolge "und" gefunden. Dann werden die einzelnen Zeichen geändert:
undbig[i] = 'U'; undbig[i+1] -= 32; /* 'n' - 32 */ undbig[i+2] -= 32; /* 'd' - 32 */
Sehen Sie sich zum besseren Verständnis auch die ASCII-Tabelle in Anhang A.2 an. Sehen Sie nach, welche Dezimalwerte die Zeichen 'n' und 'd' haben. In Dezimalwerten würden die Subtraktionen der Werte so aussehen:
110-32=78 und 100-32=68
In der ASCII-Tabelle sind 'N' = 78 und 'D' = 68.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.