Comment les chaînes C sont allouées en mémoire?

Disons que j’ai une fonction simple qui renvoie une chaîne en C de cette façon:

const char * getSsortingng() { const char * ptr = "blah blah"; return ptr; } 

et j’appelle getSsortingng () depuis main () de cette façon:

  const char * s = getSsortingng(); 

1) Selon gdb, la variable ptr est stockée sur la stack, mais la chaîne pointée par ptr n’est pas:

 (gdb) p &ptr $1 = (const char **) 0x7fffffffe688 (gdb) p ptr $2 = 0x4009fc "blah blah" 

Est-ce que cela signifie que “bla bla” n’est pas une variable locale dans getSsortingng ()?

J’imagine que s’il s’agissait d’une variable locale, je ne pourrais pas la transmettre à ma fonction main () … Mais si ce n’est pas le cas, où est-elle stockée? Sur le tas? Est-ce une “sorte” d’allocation de mémoire dynamic implémentée par le système d’exploitation chaque fois qu’il frappe une chaîne, ou quoi?

2) Si j’utilise un tableau au lieu d’un pointeur, de cette façon:

 const char *getSsortingng2() { const char a[] = "blah blah blah"; return a; } 

le compilateur me prévient que:

warning: address of local variable 'a' returned

(et bien sûr le programme comstack, mais ça ne marche pas).

En fait, si je demande à gdb, je reçois

 (gdb) p &a $2 = (const char (*)[15]) 0x7fffffffe690 

Mais je pensais que const char * ptr et const char a [] étaient fondamentalement la même chose. On dirait qu’ils ne sont pas.

Ai-je tort? Quelle est exactement la différence entre les deux versions?

Je vous remercie!

Quand tu écris

 const char *ptr = "blah blah"; 

Ensuite, le compilateur génère une chaîne constante (de type char [] ) avec le contenu "blah blah" et la stocke quelque part dans le segment de données de l’exécutable (sa durée de stockage est similaire à celle des variables déclarées à l’aide de le mot clé static ).

Ensuite, l’adresse de cette chaîne, qui est valide pendant toute la durée de vie du programme, est stockée dans le pointeur ptr , qui est ensuite renvoyé. Tout est bon.

Est-ce que cela signifie que "blah blah" n’est pas une variable locale dans getSsortingng ()?

Permettez-moi de répondre avec une phrase anglaise brisée: oui, ce n’est pas.

Cependant, lorsque vous déclarez un tableau, comme dans

 const char a[] = "blah blah"; 

alors le compilateur ne génère pas de chaîne statique. (En effet, il s’agit d’un cas un peu particulier lors de l’initialisation de chaînes.) Il génère ensuite un code qui allouera une assez grande quantité de mémoire de stack pour le tableau a (ce n’est pas un pointeur!) Et le remplira des octets de la chaîne. Ici, a est en fait une variable locale et son adresse renvoyée entraîne un comportement indéfini.

Alors…

Mais je pensais que const char *ptr et const char a[] étaient fondamentalement la même chose.

Non, pas du tout, car les tableaux ne sont pas des pointeurs .

J’imagine que s’il s’agissait d’une variable locale, je ne pourrais pas la transmettre à ma fonction main () … Mais si ce n’est pas le cas, où est-elle stockée?

Les littéraux de chaîne sont généralement stockés dans une section de données en lecture seule ( .rodata ). C standard dit simplement qu’ils ont une durée de stockage statique. Par conséquent, vous pouvez renvoyer un pointeur sur un tel littéral, mais ce n’est pas le cas des tableaux.

Dans l’exemple suivant, l’object pointé par p1 a une durée de stockage statique, alors que le tableau p2 a une durée de stockage automatique.

 char *f(void) { const char *p1 = "hello, world"; char p2[] = "hello, world"; return p1; /* allowed */ return p2, /* forbidden */ } 

Vous avez raison de dire que ce n’est pas la même chose. char a [] est un tableau formé sur la stack, puis rempli avec “blah ..” – dans la fonction, vous avez essentiellement `const char a [15]; strcpy (a, “bla bla bla”); ”

The const char *ptr = "blah blah blah"; d’autre part est simplement un pointeur (le pointeur lui-même est sur la stack), et le pointeur pointe vers la chaîne “blah blah blah”, qui est stockée ailleurs [dans “lecture seule” le plus probable].

Vous remarquerez une grande différence si vous essayez de modifier quelque chose, par exemple: a[2] = 'e'; vs ptr[2] = 'e'; – le premier réussira, car vous modifiez une valeur de stack, où le second échouera (probablement), car vous modifiez une mémoire en lecture seule, ce qui ne devrait bien entendu pas fonctionner.

Dans votre fonction, la scope d’ a[] tableau a[] est getSsortingng2() dans la fonction getSsortingng2() . sa variable de tableau local .

 const char *getSsortingng2() { const char a[] = "blah blah blah"; return a; } 

Dans la casse ci-dessus, "blah blah blah" copie poing dans a[] et vous essayez de renvoyer ce tableau en return a instruction, mais pas une chaîne constante.

Où comme dans le premier code getSsortingng() : ptr = "blah blah"; ptr pointe sur une mémoire de scope globale.

 const char * getSsortingng() { const char * ptr = "blah blah"; return ptr; } 

Dans ce cas, vous retournez l’adresse de la chaîne constante "blah blah" qu’il est légal de faire.

Donc, en fait, son problème de scope.

il est utile d’en savoir plus sur la disposition en mémoire des programmes C et la scope variable en C

Ils ne sont pas les mêmes.

Le premier est un pointeur sur un littéral de chaîne. Le pointeur lui-même est en stockage automatique. La chaîne est en mémoire statique, en lecture seule. C’est immuable.

Le second est un tableau de caractères automatique (stack) (et ce retour n’est, comme le dit l’avertissement, pas légal).