Menu

Les chaînes en C : tableaux de char, terminateur nul et exemples

Le C n'a pas de type chaîne - une chaîne est un tableau de char se terminant par un octet '\0'. Cette page construit ce modèle mental, puis couvre les littéraux, l'affichage, le parcours, et pourquoi on ne peut pas copier une chaîne avec =.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

La plupart des langages vous donnent un type string qui connaît sa propre longueur. Le C non. En C, une chaîne n'est qu'un tableau de char avec une règle attachée : le texte est suivi d'un octet de valeur zéro, écrit '\0' et appelé le terminateur nul. Tout le reste des chaînes C découle de cette seule convention.

Une chaîne est un tableau de char plus '\0'

Déclarez une chaîne en initialisant un tableau de char depuis un littéral :

Le tableau fait quatre octets, pas trois :

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

sizeof(name) vaut 4. Le compilateur a compté les caractères, en a ajouté un pour le terminateur, et a dimensionné le tableau pour vous. Cet octet supplémentaire explique pourquoi char name[3] = "Ada"; est un bug en gestation - le texte tient mais pas le terminateur, donc rien en aval ne peut dire où la chaîne se termine.

Quand vous voulez de la place pour changer le contenu plus tard, donnez la taille vous-même et laissez du mou :

char name[32] = "Ada";   /* 3 caracteres, un terminateur, 28 octets de marge */

Les octets inutilisés sont mis à zéro par l'initialiseur, ce qui est inoffensif.

Pourquoi le terminateur compte

Aucune fonction C ne reçoit une longueur. printf("%s", s) reçoit une adresse et avance en affichant les octets jusqu'à rencontrer '\0'. Donc un tableau de char sans terminateur n'est pas une chaîne, et le traiter comme tel lit la mémoire qui se trouve après.

La règle à mémoriser : sizeof vous donne le stockage, strlen vous donne le texte. Pour char buf[32] = "Ada", sizeof buf vaut 32 et strlen(buf) vaut 3. Elles répondent à des questions différentes et les confondre est l'un des bugs C les plus courants.

Littéraux de chaîne contre tableaux de char

Ces deux lignes se ressemblent et se comportent très différemment :

char  a[] = "hello";   /* un tableau qui vous appartient, initialise depuis le litteral */
char *p   = "hello";   /* un pointeur vise le litteral lui-meme */

a est un tableau de 6 octets contenant une copie privée du texte. Vous pouvez le modifier. p pointe dans les données de chaînes en lecture seule du programme ; le littéral est partagé, et écrire à travers p est un comportement indéfini qui plante typiquement à l'exécution plutôt qu'à la compilation.

Écrivez const char * dès qu'un pointeur vise un littéral. Le compilateur rejette alors l'écriture à la compilation au lieu de la laisser devenir un plantage. Choisir entre les deux est en réalité une question de pointeurs et de tableaux, couverte dans pointeurs et tableaux.

Une autre différence à connaître : sizeof a vaut 6, mais sizeof p est la taille d'un pointeur (8 sur la plupart des systèmes), quelle que soit la longueur du texte.

Afficher et lire des chaînes

%s affiche une chaîne ; %c affiche un seul caractère.

Pour l'entrée, évitez complètement gets - il a été retiré du langage parce qu'il ne peut pas être utilisé sans risque. Utilisez fgets, qui prend la taille du tampon et ne débordera pas :

fgets conserve le saut de ligne que vous avez tapé, ce que vous ne voulez presque jamais dans la chaîne. La ligne strcspn trouve le premier '\n' et l'écrase avec un terminateur - une façon compacte et standard de le retirer.

Parcourir une chaîne caractère par caractère

Parce que la fin est marquée plutôt que comptée, la boucle idiomatique teste le caractère lui-même :

La condition text[i] != '\0' est souvent abrégée en text[i], puisque '\0' vaut zéro et est donc faux. Les deux sont correctes ; la forme explicite est plus facile à lire pendant l'apprentissage.

Vous pouvez parcourir avec un pointeur plutôt qu'un indice, ce qui est tout aussi idiomatique en C :

Le cast en unsigned char avant d'appeler toupper n'est pas décoratif : les fonctions de <ctype.h> sont indéfinies pour des valeurs négatives, et un char simple peut être négatif pour les octets au-dessus de 127.

On ne peut pas copier une chaîne avec =

C'est le mur que tout nouveau venu rencontre :

char a[10];
a = "hello";        /* erreur : affectation a une expression de type tableau */

char b[10] = "hi";
char c[10];
c = b;              /* erreur - meme raison */

Un nom de tableau n'est pas une valeur modifiable, donc = n'a rien à faire. Le seul endroit où le raccourci fonctionne est l'initialisation à la déclaration, une opération différente effectuée par le compilateur.

Pour copier à l'exécution, copiez les octets avec strcpy de <string.h> :

Le même raisonnement explique pourquoi == ne compare pas les chaînes. if (a == b) compare deux adresses, donc c'est faux pour deux tableaux différents contenant un texte identique. Utilisez strcmp(a, b) == 0 à la place. Les deux fonctions et leurs parentes plus sûres sont couvertes dans les fonctions de chaînes.

Les tableaux de chaînes

Une liste de chaînes est soit un tableau de pointeurs (pour du texte fixe que vous ne faites que lire), soit un tableau de char à deux dimensions (quand chaque entrée doit être modifiable) :

Le tableau de pointeurs stocke trois adresses vers des littéraux en lecture seule - compact, mais non modifiable. La forme char[3][16] réserve 48 octets de votre propre mémoire, chaque ligne peut donc être réécrite.

Erreurs courantes

  • Oublier l'octet du terminateur. char s[5] = "hello" n'a pas de place pour '\0'. Dimensionnez pour strlen + 1.
  • Utiliser sizeof là où vous vouliez strlen. sizeof sur un pointeur donne la taille du pointeur, pas la longueur du texte.
  • Écrire à travers un char * vers un littéral. Déclarez ces pointeurs const char *.
  • Comparer avec ==. Cela compare des adresses. Utilisez strcmp.
  • Lire avec scanf("%s", buf). Il n'a aucune limite de taille et déborde allègrement ; préférez fgets.

Questions fréquentes

Comment déclare-t-on une chaîne en C ?

Comme un tableau de char : char name[] = "Ada";. Le C n'a pas de type chaîne intégré - ce tableau contient 'A', 'd', 'a' et un quatrième octet caché, '\0', qui marque la fin. Si vous voulez de la place pour changer le texte plus tard, donnez au tableau une taille explicite : char name[32] = "Ada";.

Qu'est-ce que le terminateur nul en C ?

L'octet '\0' (de valeur zéro) qui marque où une chaîne se termine. Toute fonction standard - printf("%s"), strlen, strcpy - avance jusqu'à trouver cet octet. Un tableau de char sans lui n'est pas une chaîne, et le passer à ces fonctions lit au-delà de la fin de votre mémoire.

Pourquoi ne peut-on pas affecter une chaîne avec = en C ?

Parce qu'un tableau n'est pas une valeur copiable avec =. char a[10]; a = "hi"; ne compilera pas. Copiez plutôt les caractères avec strcpy(a, "hi") de <string.h>, ou initialisez à la déclaration - char a[10] = "hi"; - le seul endroit où le raccourci fonctionne.

Quelle est la différence entre char * et char [] en C ?

char s[] = "hi" crée un tableau modifiable contenant une copie privée du texte. char *p = "hi" crée un pointeur vers un littéral de chaîne, qui vit en mémoire en lecture seule - le lire est sans problème, écrire à travers lui plante. Utilisez le tableau quand vous comptez modifier, le pointeur quand vous ne faites que lire.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER