Je voudrais savoir pourquoi le code suivant est légal
char *string = "hello"; string = "changed"; string = "changed again";
Cela ne pose aucun problème avec le compilateur.
Mais j'avais l'impression que char les tableaux initialisés en tant que pointeur (char * string par opposition à char string []) sont en lecture seule et ne peuvent pas être écrasés?
3 Réponses :
Pensez-y (explications à suivre). Il existe de grandes différences entre
string5[0] = 'H'; /* WRONG */
et
char *string5 = "hello"; char *string6 = "world"; strcpy(string5, string6); /* WRONG */
et
+-----------+ +---+---+---+---+---+---+ string1: | * | | h | e | l | l | o |\0 | +-----|-----+ +---+---+---+---+---+---+ | +--------------+ | V +-----------+ +---+---+---+---+---+---+ string2: | *----------->| w | o | r | l | d |\0 | +-----------+ +---+---+---+---+---+---+
et
string1 = string2;
Assurez-vous de comprendre en quoi les cas 1/2 et 3/4 sont différents, mais les deux fonctionnent. Assurez-vous de comprendre pourquoi les cas 5/6 et 7/8 sont erronés et ne fonctionnent pas (mais pour des raisons différentes).
Explications:
Après
+-----------+ +---+---+---+---+---+---+ string1: | *----------->| h | e | l | l | o |\0 | +-----------+ +---+---+---+---+---+---+ +-----------+ +---+---+---+---+---+---+ string2: | *----------->| w | o | r | l | d |\0 | +-----------+ +---+---+---+---+---+---+
vous avez deux tableaux initialisés en mémoire qui ressemblent à ceci:
char *string1 = "hello"; char *string2 = "world";
Et après avoir appelé
+---+---+---+---+---+---+ string3: | w | o | r | l | d |\0 | +---+---+---+---+---+---+ +---+---+---+---+---+---+ string4: | w | o | r | l | d |\0 | +---+---+---+---+---+---+
vous vous retrouvez avec
strcpy(string3, string4);
strcpy
caractères copiés de array4
vers array3
. (Notez également que les string3
et string4
d'origine avaient la même longueur. Cela aurait également fonctionné si string4
avait été plus court, mais si string4
avait été plus long, il y aurait eu un débordement de tableau lors de sa copie dans string3
.)
Passons maintenant à la casse du pointeur. Après
+---+---+---+---+---+---+ string3: | h | e | l | l | o |\0 | +---+---+---+---+---+---+ +---+---+---+---+---+---+ string4: | w | o | r | l | d |\0 | +---+---+---+---+---+---+
, vous avez deux tableaux anonymes, généralement en mémoire en lecture seule, pointés par deux variables de pointeur qui ressemblent à ceci:
char string3[] = "hello"; char string4[] = "world";
Les données de chaîne peuvent être disposées comme ceci en mémoire:
Addr Contents ---- -------- 1000 h e l l o \0 1006 c h a n g e d \0 1014 c h a n g e d a g a i n \0
Lorsque vous initialisez la variable string
, elle contient l'adresse 1000 code>. La première réaffectation le change en
1006
, la seconde le change en 1014
. Les données de chaîne elles-mêmes ne sont pas écrasées.
"Mais j'avais l'impression que les tableaux de caractères étaient initialisés en tant que pointer (char * string par opposition à char string []) sont en lecture seule et ne peut pas être écrasé? "
C'est "en quelque sorte" vrai, selon la façon dont vous le voyez. Lorsque le compilateur atteint une affectation à un littéral, il met de côté un peu de mémoire pour le littéral et pointe votre char *
vers celui-ci. En fonction du compilateur et de l'architecture, vous ne pourrez peut-être pas modifier cette mémoire. Dans tous les cas, vous ne devez jamais essayer de changer cette mémoire (après tout, vous ne savez pas où elle se trouve). De cette façon, la mémoire dans laquelle la chaîne littérale (les valeurs réelles des caractères) est stockée peut très bien être en lecture seule.
Dans le cas que vous avez montré, vous ne changez pas la mémoire contenant les caractères. Vous modifiez l'adresse vers laquelle pointe string
. En supposant que vous définissiez string
dans une fonction, la valeur sera allouée sur la pile. Ce sera sizeof (void *)
et cette mémoire peut être changée… elle est changée lorsque vous changez vers quoi pointe string
. Vous modifiez l'adresse où «changé» est stocké à l'adresse où «changé à nouveau» est stocké.
Vous n'écrasez pas le tableau, vous réaffectez le pointeur.
La variable
string
est juste un pointeur verschar
, vous changez simplement le caractère vers lequel elle pointe.Vous n'êtes pas autorisé à faire
strcpy (string, "changed");
, cela écraserait le tableau.c'est ce que j'ai supposé, mais lorsque je débogue ce code, je remarque que les adresses mémoire sont extrêmement proches. comme si je concaténais la chaîne nouvellement affectée à l'ancienne chaîne
Et cela n'a rien à voir avec l'utilisation ou non d'un pointeur, c'est parce qu'il pointe vers une chaîne littérale plutôt qu'une chaîne inscriptible.
@Jules Le compilateur place toutes les données de chaîne les unes à côté des autres en mémoire.
Si vous ouvrez votre exécutable avec un éditeur de texte, vous pouvez voir les mots quelque part dans le bruit binaire. Une fois le programme chargé en mémoire, démarre et l'assignation est exécutée, votre pointeur pointe vers ces octets.
string
pointe vers un emplacement en mémoire, vous changez simplement cet emplacement.