Menu

Zeichenketten in C: char-Arrays, der Nullterminator und Beispiele

C hat keinen Zeichenkettentyp - eine Zeichenkette ist ein char-Array, das mit einem '\0'-Byte endet. Diese Seite baut dieses Denkmodell auf und behandelt dann Literale, Ausgabe, Durchlaufen und warum man eine Zeichenkette nicht mit = kopieren kann.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Die meisten Sprachen geben dir einen string-Typ, der seine eigene Länge kennt. C nicht. In C ist eine Zeichenkette nur ein Array von char mit einer angehängten Regel: Auf den Text folgt ein Byte mit dem Wert null, geschrieben '\0' und Nullterminator genannt. Alles andere an C-Zeichenketten folgt aus dieser einen Konvention.

Eine Zeichenkette ist ein char-Array plus '\0'

Deklariere eine Zeichenkette, indem du ein char-Array aus einem Literal initialisierst:

Das Array hat vier Bytes, nicht drei:

name:  +-----+-----+-----+------+
       | 'A' | 'd' | 'a' | '\0' |
       +-----+-----+-----+------+
Index:    0     1     2     3

sizeof(name) ist 4. Der Compiler hat die Zeichen gezählt, eines für den Terminator hinzugefügt und das Array für dich bemessen. Dieses zusätzliche Byte ist der Grund, warum char name[3] = "Ada"; ein Bug im Wartestand ist - der Text passt, der Terminator nicht, und nichts weiter unten kann sagen, wo die Zeichenkette endet.

Wenn du später Platz zum Ändern des Inhalts willst, gib die Größe selbst an und lass Luft:

char name[32] = "Ada";   /* 3 Zeichen, ein Terminator, 28 Bytes übrig */

Die ungenutzten Bytes werden vom Initialisierer mit null gefüllt, was harmlos ist.

Warum der Terminator zählt

Keine C-Funktion erhält eine Länge. printf("%s", s) bekommt eine Adresse gereicht und läuft vorwärts und gibt Bytes aus, bis es auf '\0' trifft. Ein char-Array ohne Terminator ist also keine Zeichenkette, und es als eine zu behandeln liest den Speicher, der zufällig folgt.

Die Regel zum Merken: sizeof gibt dir den Speicher, strlen gibt dir den Text. Für char buf[32] = "Ada" ist sizeof buf gleich 32 und strlen(buf) gleich 3. Sie beantworten verschiedene Fragen, und sie zu verwechseln ist einer der häufigsten Bugs in C.

Zeichenkettenliterale gegen char-Arrays

Diese beiden Zeilen sehen ähnlich aus und verhalten sich sehr verschieden:

char  a[] = "hallo";   /* ein Array, das dir gehört, aus dem Literal initialisiert */
char *p   = "hallo";   /* ein Zeiger, der auf das Literal selbst zielt */

a ist ein 6-Byte-Array mit einer eigenen Kopie des Textes. Du darfst es ändern. p zeigt in die nur lesbaren Zeichenkettendaten des Programms; das Literal wird geteilt, und durch p zu schreiben ist undefiniertes Verhalten, das typischerweise zur Laufzeit abstürzt statt zur Übersetzungszeit.

Schreibe const char *, wann immer ein Zeiger auf ein Literal zielt. Der Compiler weist den Schreibzugriff dann zur Übersetzungszeit zurück, statt ihn zu einem Absturz werden zu lassen. Zwischen beiden zu wählen ist eigentlich eine Frage zu Zeigern und Arrays, behandelt unter Zeiger und Arrays.

Ein weiterer wissenswerter Unterschied: sizeof a ist 6, aber sizeof p ist die Größe eines Zeigers (auf den meisten Systemen 8), unabhängig davon, wie lang der Text ist.

Zeichenketten ausgeben und einlesen

%s gibt eine Zeichenkette aus; %c gibt ein einzelnes Zeichen aus.

Zur Eingabe meide gets vollständig - es wurde aus der Sprache entfernt, weil es sich nicht sicher benutzen lässt. Nimm fgets, das die Puffergröße entgegennimmt und nicht überläuft:

fgets behält den Zeilenumbruch, den du gedrückt hast, und das ist fast nie das, was du in der Zeichenkette haben willst. Die strcspn-Zeile findet das erste '\n' und überschreibt es mit einem Terminator - eine kompakte, standardkonforme Art, ihn abzuschneiden.

Eine Zeichenkette Zeichen für Zeichen durchlaufen

Weil das Ende markiert und nicht gezählt ist, prüft die idiomatische Schleife das Zeichen selbst:

Die Bedingung text[i] != '\0' wird oft zu bloß text[i] verkürzt, da '\0' null und damit falsch ist. Beides ist korrekt; die ausdrückliche Form liest sich beim Lernen leichter.

Du kannst statt mit einem Index auch mit einem Zeiger laufen, was genauso idiomatisches C ist:

Der Cast nach unsigned char vor dem Aufruf von toupper ist keine Zierde: Die Funktionen aus <ctype.h> sind für negative Werte undefiniert, und ein schlichtes char kann für Bytes über 127 negativ sein.

Du kannst eine Zeichenkette nicht mit = kopieren

Das ist die Wand, an die jeder Neuling stößt:

char a[10];
a = "hallo";        /* Fehler: Zuweisung an einen Ausdruck mit Arraytyp */

char b[10] = "hi";
char c[10];
c = b;              /* Fehler - derselbe Grund */

Ein Arrayname ist kein änderbarer Wert, = hat also nichts zu tun. Die einzige Stelle, an der die Kurzform funktioniert, ist die Initialisierung bei der Deklaration, und das ist eine andere Operation, die der Compiler ausführt.

Um zur Laufzeit zu kopieren, kopiere die Bytes mit strcpy aus <string.h>:

Dieselbe Überlegung erklärt, warum == keine Zeichenketten vergleicht. if (a == b) vergleicht zwei Adressen, es ist also für zwei verschiedene Arrays mit identischem Text falsch. Nimm stattdessen strcmp(a, b) == 0. Beide Funktionen und ihre sichereren Verwandten werden unter Zeichenkettenfunktionen behandelt.

Arrays von Zeichenketten

Eine Liste von Zeichenketten ist entweder ein Array von Zeigern (für festen Text, den du nur liest) oder ein zweidimensionales char-Array (wenn jeder Eintrag änderbar sein muss):

Das Zeigerarray speichert drei Adressen in nur lesbare Literale - kompakt, aber nicht änderbar. Die Form char[3][16] reserviert 48 Bytes deines eigenen Speichers, sodass jede Zeile neu beschrieben werden kann.

Häufige Fehler

  • Das Byte des Terminators vergessen. char s[5] = "hallo" hat keinen Platz für '\0'. Bemiss auf strlen + 1.
  • sizeof benutzen, wo strlen gemeint war. sizeof auf einem Zeiger gibt die Größe des Zeigers, nicht die Länge des Textes.
  • Durch ein char * in ein Literal schreiben. Deklariere solche Zeiger als const char *.
  • Mit == vergleichen. Das vergleicht Adressen. Nimm strcmp.
  • Mit scanf("%s", buf) einlesen. Es hat keine Größenbegrenzung und läuft bereitwillig über; bevorzuge fgets.

Häufig gestellte Fragen

Wie deklariert man eine Zeichenkette in C?

Als char-Array: char name[] = "Ada";. C hat keinen eingebauten Zeichenkettentyp - dieses Array hält 'A', 'd', 'a' und ein viertes verstecktes Byte, '\0', das das Ende markiert. Willst du später Platz zum Ändern, gib dem Array eine ausdrückliche Größe: char name[32] = "Ada";.

Was ist der Nullterminator in C?

Das Byte '\0' (Wert null), das markiert, wo eine Zeichenkette endet. Jede Standardfunktion - printf("%s"), strlen, strcpy - liest vorwärts, bis sie dieses Byte findet. Ein char-Array ohne eines ist keine Zeichenkette, und es diesen Funktionen zu übergeben liest über das Ende deines Speichers hinaus.

Warum kann ich eine Zeichenkette in C nicht mit = zuweisen?

Weil ein Array kein Wert ist, den man mit = kopieren kann. char a[10]; a = "hi"; kompiliert nicht. Kopiere stattdessen die Zeichen mit strcpy(a, "hi") aus <string.h>, oder initialisiere bei der Deklaration - char a[10] = "hi"; -, die eine Stelle, an der die Kurzform funktioniert.

Was ist der Unterschied zwischen char * und char [] in C?

char s[] = "hi" erzeugt ein änderbares Array mit einer eigenen Kopie des Textes. char *p = "hi" erzeugt einen Zeiger auf ein Zeichenkettenliteral, das in nur lesbarem Speicher liegt - es zu lesen ist in Ordnung, hindurchzuschreiben stürzt ab. Nimm das Array, wenn du ändern willst, den Zeiger, wenn du nur liest.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S