Pourquoi ai-je besoin de char au lieu de char pour une chaîne de longueur k?

J’ai donc un jeu de code simple:

#include  int main() { char x[3] = "ABC"; // (*) puts(x); return 0; } 

Il retourne une sortie étrange:

 ABC¬ a

En utilisant la réponse principale de cette question , j’ai constaté que lorsque j’ai remplacé x[3] par x[4] tout se passait bien.

Mais pourquoi? Pourquoi ai-je une sortie étrange sur x[3] , et pourquoi x[4] correct?

Puisque vous avez demandé “pourquoi” cela donne ABC -a , voici une explication: votre caractère char x[3] = "ABC" ne convient pas aux options de puts . puts attend une chaîne terminée par zéro. Cependant, votre x est fondamentalement:

 char x[3] = {'A', 'B', 'C'}; 

Comme vous le savez, il n’ya aucun moyen d’obtenir la longueur d’un tableau (dynamic):

 char * allocate(){ return malloc(rand() + 1); } char * mem = allocate(); // how large is mem?? 

Il n’y a aucun moyen pour vous de savoir combien de temps cela prend. Cependant, pour imprimer une chaîne qui n’est rien d’autre qu’une séquence continue de caractères en mémoire, une fonction doit savoir quand la chaîne (c’est-à-dire la séquence de caractères) se termine.

C’est pourquoi le code standard américain d’échange d’informations (ASCII) et de nombreux autres jeux de caractères contiennent le caractère null . C’est fondamentalement caractère avec la valeur 0 :

 char wrong_abc[3] = {'A', 'B', 'C'}; // when does it end? char correct_abc[4] = {'A', 'B', 'C', 0 }; // oh, there's a zero! 

Maintenant, les fonctions telles que les puts peuvent simplement vérifier 0 :

 // Simplified, actual "puts" checks for errors and returns // EOF on error or a non-negative int on succes. void puts(const char * str){ int i = 0; while(str[i] != 0){ putchar(str[i]); i++; } putchar('\n'); } 

Et c’est pourquoi tu

  • besoin de mémoire pour tous les caractères de la séquence de caractères +1,
  • obtenir un comportement indéfini lorsque vous oubliez le 0 .

L’implémentation de puts ci-dessus ne trouverait jamais 0 et laisserait accidentellement la mémoire que vous possédez (ou accédez à d’autres données), ce qui conduit généralement à une erreur de segmentation ou à d’autres erreurs (ou pire, ne sont pas détectés pendant une longue période et produisent ensuite des erreurs critiques ). Le comportement réel dans une telle situation est indéfini.

Notez que les littéraux de chaîne (par exemple "ABC" ) ont automatiquement un '\0' à la fin. En outre, le compilateur est assez intelligent pour déterminer la longueur du littéral pour vous, vous pouvez donc simplement utiliser

 char x[] = "ABC"; 

De cette façon, vous n’avez pas à vous inquiéter si vous changez le littéral plus tard.

Il n’y a pas d’espace pour le \0 . En fait, je m’attendrais à ce que la compilation échoue dans ce cas.

Essayer

 char x[4] = "ABC"; 

Ou, comme l’ a suggéré @Zeta , juste

 char x[] = "ABC"; 

Une chaîne se termine par le caractère null – vous ne lui avez pas alloué d’espace.

Faire

 char x[] = "ABC"; 

et laissez le compilateur faire le travail pour vous!

Votre chaîne doit être terminée par \0 .

Utilisation

 char x[] = "ABC"; 

au lieu.

En utilisant la réponse principale de cette question, j’ai constaté que lorsque j’ai remplacé x [3] par x [4], tout se passait bien.

MAIS POURQUOI? Que se passe-t-il pour que x [3] produise un résultat aussi étrange?

puts() continue jusqu’à rencontrer un \0 octet final. Donc, si vous n’en fournissez pas à la fin de votre chaîne, il continue à courir derrière votre chaîne jusqu’à ce qu’il trouve un \0 ou se bloque .

Voir les mises () description :

La fonction commence à copier à partir de l’adresse spécifiée (str) jusqu’à atteindre le caractère nul final (‘\ 0’) . Ce caractère nul final n’est pas copié dans le stream.

Chaque fois que vous déclarez une variable chaîne, ajoutez simplement un caractère supplémentaire à ceux dont vous avez besoin pour le caractère de fin '\0' et tout ira bien.