La première question concerne le caractère nul \ 0 à la fin de la chaîne, il y a tellement de variations en termes de quand \ 0 est nécessaire / ajouté automatiquement.
Lors de la déclaration d'un tableau de caractères, dois-je spécifier \ 0 ou non? ou dans quel cas, dois-je spécifier \ 0 , dans quel cas non? Quelqu'un peut-il donner un résumé complet? (comme dans ce post ). si vous pensez que ma question est ambiguë, alors une question plus spécifique est de déclarer une chaîne en C, quel est le meilleur moyen, est-ce char string [] = "first string" , car par exemple, de cette façon, je peux faire strcat (string, another_string) sans souci de problème de taille?
Deuxième question: j'ai
1 char a[] = "kenny";
2 char b[3];
3 strncpy(b, a, (int)(sizeof(b) - 1));
4 printf("%i\n", (int)sizeof(b)); // 3
5 printf("string length: %i\n", (int)strlen(b)); // string length: 8
6 printf("%s\n", b); // give me random stuff like kekenny or keEkenny
sizeof se comporte normalement Je viens de me perdre ce qui se passe dans la chaîne C. J'utilisais beaucoup C ++ mais je ne comprends toujours pas comment la chaîne C se comporte.
5 Réponses :
Vous devez ajouter la chaîne de terminaison \ 0 au b, d'une manière ou d'une autre. Le printf ("% s \ n", b) s'arrêtera lorsqu'il trouvera le \ 0.
Cela dépend de ce que vous avez sur la mémoire, parfois il faut s'attendre à une erreur de segmentation.
Si vous lisez la documentation de strncpy , cela indique clairement qu'il n'ajoutera pas de terminateur NUL si la taille que vous spécifiez ne l'inclut pas:
La fonction strncpy () est similaire, sauf qu'au plus n octets de src sont copié. Attention: s'il n'y a pas d'octet nul parmi les n premiers octets de src, la chaîne placée dans dest ne sera pas terminée par un nul.
Donc dans le cas suivant, vous ne copiez que 2 caractères et aucun d'eux n'est le terminateur NUL, vous devez donc l'ajouter vous-même.
strncpy(b, a, (int)(sizeof(b) - 1));
p>
Par définition, dans l'instruction:
char *strncpy (char Target_String[], const char Source_String[], size_t Max_Chars);
string contient précisément tout le contenu qu'il peut contenir:
En mémoire il ressemble à ceci:
strncpy (target, source, n); target[n] = 0;
... illustrant pourquoi la déclaration suivante:
strcat(string, anythingElse);
est comportement indéfini . (Autrement connu par certains sous le nom de démons nasaux .)
Aussi, concernant strncpy (, ,) utilisation. Comme il n'est pas garanti qu'il contienne un caractère nul après son utilisation, il est recommandé de toujours ajouter explicitement le nul à l'emplacement approprié dans la nouvelle chaîne:
|f|i|r|s|t| |s|t|r|i|n|g|\0|?|?|?| /// end of legal memory ^
Où dans le cas de votre exemple, n == (sizeof (b) - 1)
Notez que votre conversion en (int) n'est pas nécessaire dans l'expression ci-dessus lorsque vous utilisez sizeof comme type du 3ème paramètre de strncpy (, *) est size_t:
char string[] = "first string"
Utilisation pour strncat d'autre part, ajoute le caractère nul à la fin du chaîne cible résultante, annulant la nécessité d'ajouter explicitement un nul.
Merci beaucoup! mais quand je fais char a [] = "kenny"; char b [] = "confus"; strcat (a, b); printf ("% s \ n", a); il imprime toujours kenny confused , il semble donc que les 6 octets d'origine de a soient ignorés d'une manière ou d'une autre
@KennyWang - Lisez attentivement les liens que j'ai laissés décrivant comportement indéfini .
Vous devriez être plus explicite au sujet de strncpy : randomascii.wordpress.com/2013/04/03/stop-using-strncpy-alre ady
@chqrlie - Si vous suggérez que je décourage l'utilisation des fonctions de chaîne r , (déduit par le lien que vous avez fourni.) Je ne suis pas d'accord avec ce que l'auteur suggère à propos de pas < / i> en utilisant les fonctions r string . J'ai remarqué dans d'autres commentaires que vous êtes d'accord avec lui, mais je les ai trouvés très utiles à de nombreuses reprises lorsque la taille de la chaîne de destination est fixe, mais que la taille de la chaîne à copier est inconnue. Cependant, j'apprécie votre position sur le sujet. Merci.
@ryyker: Je ne sais pas à quelles fonctions de chaîne r vous faites référence, je suis particulièrement ennuyé par strncpy , pas tellement par strncat et l'article en référence a trop de biais C ++, il ne propose pas une alternative simple à strncpy avec une sémantique intuitive telle que snprintf (dest, size, "% s", src); : Copiez la chaîne si elle correspond et sinon tronquez-la, mais terminez-la toujours par un octet nul.
@chqrlie - Je sais que c'est un peu retardé, mais j'ai jeté un autre regard sur le lien que vous avez fourni ci-dessus. (3ème commentaire en partant du haut.) Cette fois, j'ai parcouru le contenu et les commentaires (avec du café cette fois :)) et j'ai vraiment apprécié le volume et la qualité du trafic de réponse que le message a reçu. J'ai également trouvé l'alternative qu'il propose pour strncpy () intéressante, mais j'ai ensuite réalisé que son application était limitée à l'utilisation de C ++ . Je ne savais pas si vous étiez au courant de cela. Avez-vous rencontré des alternatives à la fonction strncpy () implémentée en C que vous aimez? Encore une fois, j'apprécie votre point de vue.
... @chqrlie - btw, j'ai lu ceci a > . Si vous connaissez une solution de fonction personnalisée en remplacement, ce serait ma préférence par rapport aux alternatives de bibliothèque non standard.
@ryyker: J'ai mis à jour ma réponse stackoverflow.com/a/41885173/4593267 à la question avec plus de fonctions personnalisées que j'ai trouvées utile dans mes projets.
Le problème avec les chaînes C est qu'elles sont plutôt de bas niveau, et il y a un certain nombre de choses supplémentaires que vous devez garder à l'esprit, et parfois faire "à la main".
(C ++ std :: strings , en revanche, sont à peu près des types de haut niveau tout à fait normaux.)
En réponse à vos questions spécifiques:
Vous n'avez presque jamais besoin de fournir un \ 0 explicitement. La seule fois que vous le faites, c'est lorsque vous construisez une chaîne entièrement à la main. Par exemple, ce code fonctionne:
char string[] = "first string"; strcat(string, another_string);
Mais si vous omettez l'affectation explicite à str [3] , il se comportera de manière erratique. (Mais si vous ne créez pas de chaînes à la main comme ça, vous n'avez pas besoin de vous inquiéter autant.)
Vous devez être extrêmement prudent lorsque vous copiez des chaînes avec strcpy . Vous devez vous assurer que la chaîne de destination ("buffer") est suffisamment grande. Rien en C ne s'occupera jamais de cela pour vous - rien ne garantit que la destination est suffisamment grande; rien ne vous prévient si ce n'est pas assez grand. Mais si ce n'est pas assez grand, les choses les plus étranges peuvent arriver, y compris le fait que cela semble fonctionner, même si cela ne devrait pas. (Le nom formel de ceci est "comportement indéfini".)
En particulier, si vous écrivez
char str[10];
str[0] = 'c';
str[1] = 'a';
str[2] = 't';
str[3] = '\0';
printf("%s\n", str);
ce que vous avez est un bogue, pur et simple. Il n'est pas vrai que "de cette façon, vous ne vous inquiétez pas du problème de taille". Lorsque vous dites char string [] = "..." , le compilateur dimensionne la chaîne juste assez grande pour contenir l'initialiseur (et son \ 0 ), dans ce cas 13 octets pour "première chaîne" . Le [] ne signifie pas "rendre cette chaîne suffisamment grande pour tout texte que j'essaierai jamais d'y insérer".
Vous devez être encore plus prudent lors de l'utilisation de strncpy . En fait, ma recommandation est de ne pas utiliser du tout strncpy . Ce qu'il fait réellement est inhabituel, spécial, difficile à expliquer et généralement pas ce que vous voulez de toute façon. (D'une part, si vous le faites copier moins qu'une chaîne complète, cela n'ajoute pas de `\ 0 'à la destination, ce qui aide à expliquer pourquoi vous avez des choses comme" kekenny ".)
p >
Merci!! donc quand je fais char str [10] = {'a', 'b'. 'c'}; dois-je ajouter explicitement \ 0 à la fin?
Si vous le faites de cette façon, oui. Consultez également cette ancienne question .
char a [] = "kenny"; char b [3]; strcpy (b, a); printf ("% s \ n", b); ça me donne encore kenny même si je n'ai pas assigné assez de mémoire, savez-vous ce qui s'est passé? De plus, lorsque vous utilisez strcpy , est-ce que \ 0 sera automatiquement ajouté à la fin de dest ?
Oui, strcpy ajoute \ 0 . Mais comme je l'ai dit, vous devez être sûr que la mémoire tampon de destination est suffisamment grande. Si ce n'est pas le cas (et bien sûr 3 n'est pas assez grand pour "Kenny"), des choses étranges et inexplicables peuvent arriver. (La définition formelle est «comportement indéfini».) Et «bizarre et inexplicable» inclut définitivement «cela semble fonctionner même si vous ne vous y attendiez pas».
+1: ma recommandation est de ne pas utiliser du tout strncpy . Ce qu'il fait réellement est inhabituel, spécial, difficile à expliquer et généralement pas ce que vous voulez de toute façon.
Première question
Lorsque vous le faites
char b[3] = { 0 };
le compilateur réservera de la mémoire pouvant contenir exactement le texte "première chaîne" et un Terminaison NUL. Si vous imprimez la taille de la chaîne, vous obtiendrez 13. En d'autres termes - la variable ne peut pas contenir d'autres données, il est donc inutile de concaténer une autre chaîne.
Vous pourrait faire:
strncpy(b, a, (int)(sizeof(b) - 1)); b[sizeof(b) - 1] = '\0';
et ensuite vous pouvez concaténer une autre chaîne.
Deuxième question
D'abord ce qu'il faut savoir, c'est que les chaînes en C sont des tableaux de caractères contenant une terminaison NUL.
Lorsque vous faites:
printf("string length: %i\n", (int)strlen(b));
printf("%s\n", b);
vous obtenez un tableau non initialisé, c'est-à-dire que b peut contenir n'importe quoi - comme b = {? ,? ,? }
Ensuite, vous faites:
strncpy(b, a, (int)(sizeof(b) - 1));
ce qui signifie que vous copiez les 2 premiers caractères de a vers b .
Nous savons maintenant que le b est b = {'k', 'e',? } Notez que le troisième caractère de b n'est toujours pas initialisé.
Alors quand vous le faites:
char b[3];
vous utilisez b comme s'il s'agissait d'une chaîne mais ce n'est pas le cas. Il n'y a pas de terminaison NUL. Par conséquent, les fonctions ( printf , strlen ) donnent des résultats incorrects. Appeler ces fonctions avec un tableau de caractères sans une terminaison NUL est un comportement indéfini, c'est-à-dire que tout peut arriver.
Ce qui semble se produire est deux choses:
a ) Le caractère non initialisé dans b se trouve être juste un 'E' (dans l'un de vos exemples)
b) La chaîne littérale "kenny" se trouve juste être située en mémoire juste après la variable b.
Ainsi, la fonction à deux chaînes voit vraiment la chaîne "keEkenny" qui a le len 8.
Pour résoudre ce problème, vous pouvez le faire :
char string[100] = "first string";
ou faites simplement:
char string[] = "first string";
^
No size specified
car cela initialisera tout b , c'est-à-dire b = {'\ 0', '\ 0', '\ 0'}
Non, vous ne pouvez pas "faire
strcat (string, another_string)sans vous soucier du problème de taille". Il n'y a pas de place pour la concaténation.Pour que
printfpuisse imprimer la chaîne, il doit s'agir d'une chaîne terminée par un zéro. Mais votre code tronque volontairement le terminateur nul.votre code présentera comportement indéfini < / a> . Cela fonctionnera peut-être parfois, puis soudainement non.
@WeatherVane c'est pourquoi j'ai fait
strncpy (b, a, (int) (sizeof (b) - 1));parce que je concerne un problème de taille mais cela me donne toujours des éléments aléatoires dans le code ultérieur?L'exemple de votre récit est
char string [] = "first string"; strcat (string, another_string);Il n'y a pas de place carstringfait exactement 13 octets, se terminant par le terminateur'\ 0'.@ryyker 3, c'est la quantité de mémoire que je lui ai donnée
@WeatherVane si je fais
char a [] = "kenny"; char b [] = "confus"; strcat (a, b); printf ("% s \ n", a);il affiche toujourskenny confused, il semble donc que les 6 octets originaux de 'a' soient ignorés.Lorsque la chaîne est définie avec des guillemets doubles (par exemple "abc"), l'octet nul
\ 0est automatiquement ajouté."abc"sera compilé en 4 octets (hex):61 62 63 00.Eh bien, c'était tout simplement malchanceux que
bse soit juste produit pour suivreaen mémoire. Cela vous a évité d'être informé de l'erreur par, par exemple, une erreur de segmentation. Remarque à propos destrcat: "Le comportement de strcat n'est pas défini si les chaînes source et destination se chevauchent." .