La récupération de place at-elle lieu lorsque nous initialisons un tableau de caractères avec un littéral de chaîne en c?

Lorsque nous écrivons la ligne de code suivante en C,

char local_arr[] = "I am here"; 

le littéral “je suis ici” est stocké dans la partie en lecture seule de la mémoire (disons RM ). Comment je vois que c’est qu’il est stocké de manière contigu dans le RM (Est-ce vrai?). Ensuite, le tableau local_arr (c’est-à-dire le tableau local) copie cet index de tableau par index à partir de son emplacement dans RM.

Mais qu’advient- il du littéral après que local_array le copie? Est-il perdu provoquant ainsi des memory leaks? Ou y a-t-il une sorte de ramasse-miettes, comme en Java, qui nettoie les objects non référencés?

Par exemple si j’écris un morceau de code comme suit:

 for(int i=0;i<100000;i++) char local[] = "I am wasting memory"; 

ne manquerais-je pas de mémoire? Chaque itération créera-t-elle une nouvelle instance de littéraux identiques à chaque itération? Ou vont-ils tous se référer au même littéral puisque la valeur du littéral est toujours la même?

RM appartient-il à la mémoire de tas ou à un segment spécialisé dans le tas?

De plus, le tableau local est stocké dans la stack, non? Que se passe-t-il si j’utilise un tableau dynamic ou global? Qu’est-ce qui se passe ensuite?

C n’a pas de récupération de place. Par conséquent, si vous oubliez de libérer la mémoire allouée avec le désallocateur approprié, vous obtenez une fuite de mémoire.
Bien que l’on utilise parfois un ramasse-ordures conservateur comme le collecteur Boehm , cela provoque beaucoup de maux de tête supplémentaires.

Maintenant, il y a quatre types de mémoire en C:

  • Mémoire statique: Ceci est valable du début à la fin. Il existe des variantes logiquement en lecture seule (l’écriture est un comportement indéfini) et en écriture.
  • thread-local memory: similaire à la mémoire statique, mais distinct pour chaque thread. C’est une nouveauté, comme tout support de threading.
  • mémoire automatique: tout sur la stack. Il est automatiquement libéré en quittant le bloc.
  • mémoire dynamic: ce que malloc , calloc , realloc et autres retournent sur demande. Ne pas oublier de free resp. en utilisant un autre désallocateur approprié.

Votre exemple utilise la mémoire automatique pour local_arr et laisse l’implémentation libre pour l’initialiser au littéral fourni, quelle que soit la méthode la plus efficace.

 char local_arr[] = "I am here"; 

Cela peut signifier, entre autres:

  • Utiliser memcpy / strcpy et mettre le littéral en mémoire statique.
  • Construire le tableau sur la stack en poussant les pièces, le plaçant ainsi dans les instructions exécutées.
  • Tout le rest jugé opportun.

Également d’intérêt, les littéraux C constants n’ont pas d’identité et peuvent donc partager un espace.
Quoi qu’il en soit, en utilisant la règle as-if, il est souvent possible d’optimiser les variables statiques (et même dynamics / automatiques).

Les tableaux sont stockés dans cml (c.-à-d. Emplacements de mémoire contigus) en fonction de leur type d’étendue. Par exemple, les tableaux globaux (statiques) seraient enregistrés dans le bloc démarré par un symbole (bbs), qui fait partie du segment de données, tandis que les éléments locaux sont créés sous forme de stack dans la mémoire de l’ordinateur. C’est une chaîne, car chaque élément du tableau pointe vers le suivant, formant une séquence de caractères qui constitue la chaîne. éditer en fonction des nouvelles modifications apscopes à la question

 char str[] = "Hello World"; 

Tu fais:

 char str[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0'}; 

Comme le dernier caractère est '\0' / NULL / 0 vous ne remplissez pas les informations dans le dernier bloc de mémoire, où le type de données est stocké. Dans ce cas, vous allez mettre fin à la chaîne et vous ne recevrez pas de fuites. C’est comme ça que C gère les tableaux de caractères et en particulier les chaînes. Ce sont des chaînes à zéro terminal. De nombreuses fonctions telles que strlen ne fonctionnent que s’il existe un terminateur nul.

De plus, si vous utilisez des tableaux créés de manière dynamic, ils seront stockés dans le tas. Comme je sais que le tas n’est pas grand-chose, il fournit essentiellement un environnement d’allocation et gère la mémoire à cette fin.

Pas une réponse (le déduplicateur a déjà donné une bonne réponse, je pense), mais peut-être que cela illustrera votre problème…

Considérons le code C suivant:

 #include  int main() { char foo[] = "012"; /* I just do something with the array to not let the comstackr * optimize it out entirely */ for(char *p=foo; *p; ++p) { putchar(*p); } putchar('\n'); return 0; } 

avec la sortie assembleur (avec GCC sur ma machine):

 [...] .LC0: .ssortingng "012" [...] main: [...] movl .LC0(%rip), %edi 

où vous avez une chaîne dans la mémoire en lecture seule (et cette chaîne persistera du démarrage du programme jusqu’à sa sortie). Quand je change la ligne d’initialisation de foo en

  char foo[] = "0123"; 

GCC pense que cela vaut la peine de le faire de la manière suivante:

  movl $858927408, (%rsp) # write 858927408 long (4 bytes) to where the stack pointer points to movb $0, 4(%rsp) # write a 0 byte to the position 4 bytes after where the stack pointer points to 

858927408 est 0x33323130 ( 0x30 est le code ASCII pour '0' , 0x31 pour '1' et ainsi de suite); dans ce dernier cas, la chaîne n’est pas stockée dans une mémoire en lecture seule, elle est stockée dans les instructions elles-mêmes. Dans les deux cas, le tableau auquel vous avez finalement access est toujours sur la stack. Et vous ne pouvez jamais accéder au littéral chaîne en mémoire morte dans un tel cas, même s’il existe.

HTH

Les littéraux de chaîne sont stockés dans une zone statique. Lorsque vous copiez des littéraux de chaîne dans une variable locale, il y aura deux copies: zone statique et stack. La copie dans la zone statique ne sera pas supprimée. Il n’y a pas de GC en C. Mais si vous utilisez un pointeur dans une fonction, vous pouvez accéder à la chaîne.

 #include  char *returnStr() { char *p="hello world!"; return p; } char *returnStr2() { char p[]="hello world!"; return p; } int main() { char *str=NULL; char *str2=NULL; str=returnStr(); str2 = returnStr2(); printf("%s\n", str); printf("%s\n", str2); getchar(); return 0; } 

Ainsi, dans la première fonction, il imprimera une chaîne car il utilise un pointeur. Dans la deuxième fonction, la chaîne dans la stack sera supprimée, ce qui imprimera des déchets.

Le programme ne crée pas de nouvelle chaîne à chaque fois que la boucle la frappe. Une seule chaîne existe déjà et le littéral fait simplement référence à ce tableau.

Lorsque le compilateur voit un littéral de chaîne normal, il crée * un tableau statique de caractères (C11 §6.4.5 / 6, C99 §6.4.5 / 5) contenant le contenu de la chaîne et ajoute le tableau (ou le code). pour le créer) à sa sortie. *

La seule allocation qui se produit dans la fonction est avec char local_arr[] =... , qui alloue suffisamment d’espace pour une copie du contenu de la chaîne. Puisqu’il s’agit d’un local, il est effectivement libéré lorsque le contrôle quitte le bloc qui l’a défini. Et en raison de la manière dont la plupart des compilateurs implémentent le stockage automatique (même pour les tableaux), il ne peut en principe pas couler.

* (Chaque littéral peut se retrouver dans son propre tableau. Ou bien, des littéraux de chaîne identiques peuvent faire référence au même tableau. Ou, dans certains cas, le tableau peut même être entièrement éliminé. trucs et n’est pas pertinent pour la plupart des programmes bien définis.)