1
votes

Pourquoi suis-je capable de réattribuer une nouvelle chaîne à un pointeur de caractère en C comme ceci (ch * string = "hello"; string = "assign";)

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?


8 commentaires

Vous n'écrasez pas le tableau, vous réaffectez le pointeur.


La variable string est juste un pointeur vers char , 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.


3 Réponses :


6
votes

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";


0 commentaires

5
votes

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.


0 commentaires

0
votes

"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é.


0 commentaires