La chaîne AC est un tableau de caractères qui se termine par un terminateur nul .
Tous les caractères ont une valeur de table de symboles. Le terminateur nul est la valeur du symbole 0
(zéro). Il est utilisé pour marquer la fin d'une chaîne. Cela est nécessaire car la taille de la chaîne n'est stockée nulle part.
Par conséquent, chaque fois que vous allouez de la place à une chaîne, vous devez inclure suffisamment d'espace pour le caractère de terminaison nul. Votre exemple ne fait pas cela, il alloue uniquement de la place pour les 5 caractères de "hello"
. Le code correct doit être:
char str[6] = "hello";
Ou de manière équivalente, vous pouvez écrire du code auto-documenté pour 5 caractères plus 1 terminateur nul:
char str[5+1] = "hello";
Lors de l'allocation dynamique de mémoire pour une chaîne au moment de l'exécution, vous devez également allouer de l'espace pour le terminateur nul:
char input[n] = ... ;
...
char* str = malloc(strlen(input) + 1);
Si vous n'ajoutez pas de terminateur nul à la fin d'une chaîne, les fonctions de bibliothèque qui attendent une chaîne ne fonctionneront pas correctement et vous obtiendrez des bogues de "comportement indéfini" tels que la sortie des ordures ou les plantages du programme.
La façon la plus courante d'écrire un caractère de terminaison NULL dans C est à l'aide d' un soi-disant « séquence d'échappement octal », qui ressemble à ceci: '\0'
. Cela équivaut à 100% à l'écriture 0
, mais le \
sert de code auto-documenté pour indiquer que le zéro est explicitement destiné à être un terminateur nul. Un code tel que if(str[i] == '\0')
vérifiera si le caractère spécifique est le terminateur nul.
Veuillez noter que le terme null terminator n'a rien à voir avec les pointeurs null ou la NULL
macro! Cela peut être déroutant - des noms très similaires mais des significations très différentes. C'est pourquoi le terminateur nul est parfois appelé NUL
un L, à ne pas confondre avec NULL
ou des pointeurs nuls. Voir les réponses à cette question SO pour plus de détails.
Le "hello"
dans votre code est appelé un littéral de chaîne . Cela doit être considéré comme une chaîne en lecture seule. La ""
syntaxe signifie que le compilateur ajoutera automatiquement un terminateur nul à la fin du littéral de chaîne. Donc, si vous imprimez, sizeof("hello")
vous obtiendrez 6, pas 5, car vous obtenez la taille du tableau, y compris un terminateur nul.
Il compile proprement avec gcc
En effet, pas même un avertissement. Cela est dû à un détail / défaut subtil dans le langage C qui permet d'initialiser les tableaux de caractères avec un littéral de chaîne qui contient exactement autant de caractères qu'il y a de place dans le tableau, puis de supprimer silencieusement le terminateur nul (C17 6.7.9 / 15). Le langage se comporte volontairement comme ceci pour des raisons historiques, voir Diagnostic gcc incohérent pour l'initialisation de la chaîne pour plus de détails. Notez également que C ++ est différent ici et ne permet pas d'utiliser cette astuce / faille.
char str[] = "hello";
cas.char *str = "hello";
...str[0] = foo;
problème.sizeof
à son utilisation sur un paramètre de fonction, en particulier lorsqu'il est défini comme un tableau.De la norme C (7.1.1 Définitions des termes)
Dans cette déclaration
le littéral de chaîne
"hello"
a la représentation interne commeil a donc 6 caractères, y compris le zéro de fin. Ses éléments sont utilisés pour initialiser le tableau de caractères
str
qui réserve de l'espace uniquement pour 5 caractères.La norme C (opposée à la norme C ++) permet une telle initialisation d'un tableau de caractères lorsque le zéro de fin d'un littéral de chaîne n'est pas utilisé comme initialiseur.
Cependant, par conséquent, le tableau de caractères
str
ne contient pas de chaîne.Si vous voulez que le tableau contienne une chaîne, vous pouvez écrire
ou juste
Dans le dernier cas, la taille du tableau de caractères est déterminée à partir du nombre d'initialiseurs du littéral de chaîne qui est égal à 6.
la source
Peut toutes les chaînes sont considérées comme un tableau de caractères ( Oui ), peuvent tous les tableaux de caractères sont considérés comme des chaînes ( No ).
Pourquoi pas? et pourquoi est-ce important?
Outre les autres réponses expliquant que la longueur d'une chaîne n'est stockée nulle part dans le cadre de la chaîne et les références à la norme où une chaîne est définie, le revers est "Comment les fonctions de la bibliothèque C gèrent-elles les chaînes?"
Bien qu'un tableau de caractères puisse contenir les mêmes caractères, il s'agit simplement d'un tableau de caractères à moins que le dernier caractère ne soit suivi du caractère de terminaison nul . Ce caractère de terminaison nul est ce qui permet au tableau de caractères d'être considéré (traité comme) comme une chaîne.
Toutes les fonctions en C qui attendent une chaîne comme argument s'attendent à ce que la séquence de caractères soit terminée par nul . Pourquoi?
Cela a à voir avec le fonctionnement de toutes les fonctions de chaîne. Étant donné que la longueur n'est pas incluse dans le cadre d'un tableau, les fonctions de chaîne parcourent le tableau vers l'avant jusqu'à ce que le caractère nul (par exemple
'\0'
, équivalent à décimal0
) soit trouvé. Voir le tableau et la description ASCII . Peu importe si vous utilisezstrcpy
,strchr
,strcspn
, etc .. Toutes les fonctions de chaîne reposent sur la NUL de terminaison caractère étant présent pour définir où la fin de cette chaîne est.Une comparaison de deux fonctions similaires de
string.h
soulignera l'importance du caractère de terminaison nul . Prends pour exemple:La
strcpy
fonction copie simplement les octets desrc
àdest
jusqu'à ce que le caractère de fin nul soit trouvé indiquantstrcpy
où arrêter la copie des caractères. Prenez maintenant la fonction similairememcpy
:La fonction effectue une opération similaire, mais ne considère pas ou n'exige pas que le
src
paramètre soit une chaîne. Puisqu'ilmemcpy
ne peut pas simplement balayer vers l'avant dans lasrc
copie d'octets jusqu'àdest
ce qu'un caractère de fin nul soit atteint, il nécessite un nombre explicite d'octets pour copier comme troisième paramètre. Ce troisième paramètre fournitmemcpy
avec la même taille des informations quistrcpy
peuvent être dérivées simplement en balayant vers l'avant jusqu'à ce qu'un caractère de terminaison nul soit trouvé.(qui souligne également ce qui ne va pas
strcpy
(ou toute fonction qui attend une chaîne) si vous ne parvenez pas à fournir la fonction avec une chaîne terminée par nul - il n'a aucune idée de l'endroit où s'arrêter et se précipitera avec plaisir sur le reste de votre segment de mémoire invocation d'un comportement indéfini jusqu'à ce qu'un caractère nul se trouve juste quelque part dans la mémoire - ou qu'une erreur de segmentation se produise)C'est pourquoi les fonctions qui attendent une chaîne terminée par nul doivent passer une chaîne terminée par nul et pourquoi c'est important .
la source
Intuitivement...
Considérez un tableau comme une variable (contient des éléments) et une chaîne comme une valeur (peut être placée dans une variable).
Ce n'est certainement pas la même chose. Dans votre cas, la variable est trop petite pour contenir la chaîne, donc la chaîne est coupée. (Les "chaînes entre guillemets" en C ont un caractère nul implicite à la fin.)
Cependant, il est possible de stocker une chaîne dans un tableau beaucoup plus grand que la chaîne.
Notez que les opérateurs d'affectation et de comparaison habituels (
=
==
<
etc.) ne fonctionnent pas comme vous pouvez vous y attendre. Mais lastrxyz
famille de fonctions est assez proche, une fois que vous savez ce que vous faites. Voir la FAQ C sur les chaînes et les tableaux .la source