Où en mémoire mes variables sont-elles stockées en C?

156

En considérant que la mémoire est divisée en quatre segments: données, tas, pile et code, où font les variables globales, les variables statiques, les types de données constantes, les variables locales (définies et déclarées dans les fonctions), les variables (dans la fonction principale), les pointeurs , et l'espace alloué dynamiquement (en utilisant malloc et calloc) est stocké en mémoire?

Je pense qu'ils seraient répartis comme suit:

  • Variables globales -------> données
  • Variables statiques -------> données
  • Types de données constantes -----> code
  • Variables locales (déclarées et définies dans les fonctions) --------> pile
  • Variables déclarées et définies dans la fonction principale -----> tas
  • Pointeurs (par exemple char *arr, int *arr) -------> tas
  • Espace alloué dynamiquement (en utilisant malloc et calloc) --------> stack

Je fais référence à ces variables uniquement du point de vue C.

Veuillez me corriger si je me trompe car je suis nouveau sur C.

starkk92
la source
4
Les types ne sont pas stockés en mémoire.
5
mainest juste une autre fonction. Les variables vont sur la pile à moins que malloc'd comme ailleurs.
simonc
4
les pointeurs sont (généralement) stockés sur la pile. La mémoire vers laquelle ils pointent (généralement alloués via malloc / calloc) est (généralement) sur le tas.
jpm
3
espace alloué dynamiquement (en utilisant malloc, calloc) --------> tas
One Man Crew
3
variables déclarées et définies dans la fonction principale -----> stack
One Man Crew

Réponses:

217

Vous en avez raison, mais celui qui a écrit les questions vous a trompé sur au moins une question:

  • variables globales -------> données (correctes)
  • variables statiques -------> données (correctes)
  • types de données constantes -----> code et / ou données. Considérez les littéraux de chaîne pour une situation où une constante elle-même serait stockée dans le segment de données et où les références à celle-ci seraient incorporées dans le code
  • variables locales (déclarées et définies dans les fonctions) --------> stack (correct)
  • les variables déclarées et définies dans la mainfonction -----> tas s'empilent également (l'enseignant essayait de vous tromper)
  • pointeurs (ex: char *arr, int *arr) -------> tas données ou pile, en fonction du contexte. C vous permet de déclarer un global ou un staticpointeur, auquel cas le pointeur lui-même se retrouverait dans le segment de données.
  • dynamiquement l' espace alloué ( en utilisant malloc, calloc, realloc) --------> pile tas

Il est à noter que «pile» est officiellement appelée «classe de stockage automatique».

dasblinkenlight
la source
6
Il convient également de mentionner que le tas n'appelle officiellement rien du tout. La mémoire allouée vient de quelque part, il n'y a pas de nom dans la norme pour ce «quelque part».
Steve Jessop
6
Sur certains systèmes (à savoir Linux et * BSD), il existe également un système allocasimilaire à l' mallocallocation de pile, mais qui le fait.
Andreas Grapentin le
Où va la variable const déclarée dans une méthode?
Mahori
@Ravi Au même endroit que le reste des constantes (point n ° 3 ci-dessus).
dasblinkenlight
J'utilise GCC 4.8.1 et il ne semble pas stocker la variable const locale à main dans le segment DATA. Voici le code et la carte mémoire pour 3 de ces programmes: Code 1: int main (void) {// char a [10] = "HELLO"; // 1 // const char a [10] = "BONJOUR"; // 2 retourne 0; } MEMORY MAP FOR CI-DESSUS: données textuelles bss dec hex filename 7264 1688 1040 9992 2708 a.exe MEMORY MAP FOR 2: text data bss dec hex filename 7280 1688 1040 10008 2718 a.exe MEMORY MAP FOR 3: text data bss dec hex filename 7280 1688 1040 10008 2718 a.exe
Mahori
124

Pour les futurs visiteurs qui pourraient être intéressés à connaître ces segments de mémoire, j'écris des points importants sur 5 segments de mémoire en C:

Quelques avertissements:

  1. Chaque fois qu'un programme C est exécuté, de la mémoire est allouée dans la RAM pour l'exécution du programme. Cette mémoire est utilisée pour stocker le code fréquemment exécuté (données binaires), les variables de programme, etc. Les segments de mémoire ci-dessous en parlent:
  2. Il existe généralement trois types de variables:
    • Variables locales (également appelées variables automatiques en C)
    • Variables globales
    • Variables statiques
    • Vous pouvez avoir des variables statiques globales ou statiques locales, mais les trois ci-dessus sont les types parents.

5 segments de mémoire en C:

1. Segment de code

  • Le segment de code, également appelé segment de texte, est la zone de mémoire qui contient le code fréquemment exécuté.
  • Le segment de code est souvent en lecture seule pour éviter le risque d'être remplacé par des bogues de programmation tels que le dépassement de tampon, etc.
  • Le segment de code ne contient pas de variables de programme comme la variable locale ( également appelée variable automatique en C ), les variables globales, etc.
  • Basé sur l'implémentation C, le segment de code peut également contenir des littéraux de chaîne en lecture seule. Par exemple, lorsque vous faites, la printf("Hello, world")chaîne "Hello, world" est créée dans le segment code / texte. Vous pouvez vérifier cela à l'aide de la sizecommande dans le système d'exploitation Linux.
  • Lectures complémentaires

Segment de données

Le segment de données est divisé en deux parties ci-dessous et se trouve généralement en dessous de la zone de tas ou dans certaines implémentations au-dessus de la pile, mais le segment de données ne se trouve jamais entre le tas et la zone de pile.

2. Segment de données non initialisé

  • Ce segment est également connu sous le nom de bss .
  • C'est la partie de la mémoire qui contient:
    1. Variables globales non initialisées (y compris les variables de pointeur)
    2. Variables globales constantes non initialisées .
    3. Variables statiques locales non initialisées .
  • Toute variable locale globale ou statique qui n'est pas initialisée sera stockée dans le segment de données non initialisé
  • Par exemple: variable globale int globalVar;ou variable locale statiquestatic int localStatic; sera stockée dans le segment de données non initialisé.
  • Si vous déclarez une variable globale et l'initialisez comme 0ouNULL alors elle irait toujours au segment de données non initialisé ou bss.
  • Lectures complémentaires

3. Segment de données initialisé

  • Ce segment stocke:
    1. Variables globales initialisées (y compris les variables de pointeur)
    2. Variables globales constantes initialisées .
    3. Variables statiques locales initialisées .
  • Par exemple: une variable globale int globalVar = 1;ou une variable locale statique static int localStatic = 1;sera stockée dans un segment de données initialisé.
  • Ce segment peut être davantage classé en zone de lecture seule initialisée et en zone de lecture-écriture initialisée . Les variables globales constantes initialisées iront dans la zone de lecture seule initialisée tandis que les variables dont les valeurs peuvent être modifiées à l'exécution iront dans la zone de lecture-écriture initialisée .
  • La taille de ce segment est déterminée par la taille des valeurs dans le code source du programme et ne change pas au moment de l'exécution .
  • Lectures complémentaires

4. Segment de pile

  • Le segment de pile est utilisé pour stocker des variables créées à l'intérieur de fonctions (la fonction peut être une fonction principale ou une fonction définie par l'utilisateur ), une variable comme
    1. Variables locales de la fonction (y compris les variables de pointeur)
    2. Arguments passés à la fonction
    3. Adresse de retour
  • Les variables stockées dans la pile seront supprimées dès la fin de l'exécution de la fonction.
  • Lectures complémentaires

5. Segment de tas

  • Ce segment doit prendre en charge l'allocation de mémoire dynamique. Si le programmeur veut allouer de la mémoire dynamique puis en C , il est fait en utilisant le malloc, callocourealloc des méthodes.
  • Par exemple, lorsque int* prt = malloc(sizeof(int) * 2)huit octets seront alloués dans le tas et l'adresse mémoire de cet emplacement sera renvoyée et stockée dans la ptrvariable. leptr variable sera sur la pile ou sur le segment de données selon la façon dont elle est déclarée / utilisée.
  • Lectures complémentaires
hagrawal
la source
Cela ne devrait-il pas être initialisé au lieu de non initialisé dans 3. Segment de données initialisé.
Suraj Jain
Re "stocké dans le segment de données non initialisé" (instances multiples): voulez-vous dire "stocké non initialisé dans le segment de données" ?
Peter Mortensen
@PeterMortensen Je veux dire les deux choses. "Toute variable locale globale ou statique qui n'est pas initialisée sera stockée dans le segment de données non initialisé"
hagrawal
comment pouvons-nous avoir une variable statique globale en C?
sous "quelques avertissements", j'ai trouvé ce point "Vous pouvez avoir des variables statiques globales ou statiques locales, mais les trois ci-dessus sont les types parents." dans lequel u fait référence au terme «statique globale». Mon point est qu'une variable statique ne peut pas être globale. c'est-à-dire que si une variable doit être globale, elle doit être accessible jusqu'à ce que l'exécution du programme soit terminée. Veuillez expliquer et aider si je me trompe.
11

Correction de vos mauvaises phrases

constant data types ----->  code //wrong

variables constantes locales -----> pile

variable constante globale initialisée -----> segment de données

variable constante globale non initialisée -----> bss

variables declared and defined in main function  ----->  heap //wrong

variables déclarées et définies dans la fonction principale -----> pile

pointers(ex:char *arr,int *arr) ------->  heap //wrong

dynamically allocated space(using malloc,calloc) --------> stack //wrong

pointeurs (ex: char * arr, int * arr) -------> la taille de cette variable de pointeur sera dans la pile.

Considérez que vous allouez de la mémoire de n octets (en utilisant mallocou calloc) dynamiquement et que vous créez ensuite une variable de pointeur pour la pointer. Maintenant que les noctets de mémoire sont dans le tas et que la variable pointeur requiert 4 octets (si la machine 64 bits 8 octets) qui sera dans la pile pour stocker le pointeur de départ des noctets du bloc de mémoire.

Remarque: les variables de pointeur peuvent pointer la mémoire de n'importe quel segment.

int x = 10;
void func()
{
int a = 0;
int *p = &a: //Now its pointing the memory of stack
int *p2 = &x; //Now its pointing the memory of data segment
chat *name = "ashok" //Now its pointing the constant string literal 
                     //which is actually present in text segment.
char *name2 = malloc(10); //Now its pointing memory in heap
...
}

espace alloué dynamiquement (en utilisant malloc, calloc) --------> tas

rashok
la source
les pointeurs peuvent être soit dans la pile, soit dans le tas (voir notamment: pointeurs vers des pointeurs)
argentage
@airza: maintenant mis à jour. ACtually je mettais à jour ces détails seulement :)
rashok
Dans la carte mémoire suivante, pourriez-vous indiquer où se trouvent la pile et le tas? Je ne suis pas sûr que cette question soit correcte car la pile et la mémoire peuvent ne s'appliquer qu'au moment de l'exécution. MEMORY MAP: "text data bss dec hex filename 7280 1688 1040 10008 2718 a.exe"
Mahori
7

Une architecture de bureau populaire divise la mémoire virtuelle d'un processus en plusieurs segments :

  • Segment de texte: contient le code exécutable. Le pointeur d'instruction prend des valeurs dans cette plage.

  • Segment de données: contient des variables globales (c'est-à-dire des objets avec liaison statique). Subdivisé en données en lecture seule (telles que les constantes de chaîne) et en données non initialisées ("BSS").

  • Segment de pile: contient la mémoire dynamique du programme, c'est-à-dire le magasin libre ("tas") et les cadres de pile locale pour tous les threads. Traditionnellement, la pile C et le tas C se développaient dans le segment de pile à partir d'extrémités opposées, mais je crois que cette pratique a été abandonnée parce qu'elle est trop dangereuse.

Le programme AC place généralement des objets avec une durée de stockage statique dans le segment de données, des objets alloués dynamiquement sur le magasin gratuit et des objets automatiques sur la pile d'appels du thread dans lequel il vit.

Sur d'autres plates-formes, comme l'ancien mode réel x86 ou sur les appareils embarqués, les choses peuvent évidemment être radicalement différentes.

Kerrek SB
la source
"Je crois que la pratique a été abandonnée parce qu'elle est trop dangereuse" - et rend impossible la mise en œuvre de threads, depuis lors, vous avez besoin de plus d'une pile par programme et ils ne peuvent pas tous être à la fin :-)
Steve Jessop
@SteveJessop: Oui, je pensais ça aussi. Mais les fils existent depuis longtemps - je ne sais pas si toutes les piles de fils ont également poussé à l'envers, ou si elles grandiraient comme le tas ... de toute façon, de nos jours, tout va dans le même sens et il y a des gardes pages.
Kerrek SB
6

Je fais référence à ces variables uniquement du point de vue C.

Du point de vue du langage C , tout ce qui compte, c'est l'étendue, la portée, le lien et l'accès; La façon exacte dont les éléments sont mappés sur différents segments de mémoire dépend de l'implémentation individuelle, et cela varie. La norme linguistique ne parle pas de segments de mémoire du tout . La plupart des architectures modernes agissent essentiellement de la même manière; les variables de portée de bloc et les arguments de fonction seront alloués à partir de la pile, les variables de portée de fichier et statiques seront allouées à partir d'un segment de données ou de code, la mémoire dynamique sera allouée à partir d'un tas, certaines données constantes seront stockées dans des segments en lecture seule , etc.

John Bode
la source
3

Une chose à garder à l'esprit à propos du stockage est la règle du « comme si » . Le compilateur n'est pas obligé de placer une variable à un endroit spécifique - au lieu de cela, il peut la placer où bon lui semble aussi longtemps que le programme compilé se comporte comme s'il était exécuté dans la machine abstraite C selon les règles de la machine abstraite C. Cela s'applique à toutes les durées de stockage . Par exemple:

  • une variable qui n'est pas accessible à tous peut être complètement éliminée - elle n'a pas de stockage ... n'importe où. Exemple - voyez comment il y a 42dans le code d'assemblage généré mais aucun signe de404 .
  • une variable avec une durée de stockage automatique dont l'adresse n'est pas prise n'a pas du tout besoin d'être stockée en mémoire. Un exemple serait une variable de boucle.
  • une variable qui est constou constn'a pas besoin d'être en mémoire. Exemple - le compilateur peut prouver que fooc'est effectivement le cas constet intègre son utilisation dans le code. bara un lien externe et le compilateur ne peut pas prouver qu'il ne serait pas modifié en dehors du module actuel, il n'est donc pas en ligne.
  • un objet alloué avec mallocn'a pas besoin de résider dans la mémoire allouée à partir du tas! Exemple - remarquez que le code n'a pas d'appel à mallocet que la valeur 42 n'est jamais stockée en mémoire, elle est conservée dans un registre!
  • donc un objet qui a été alloué par mallocet la référence est perdue sans désallouer l'objet avec free besoin de ne pas fuir de mémoire ...
  • l'objet alloué par mallocn'a pas besoin d'être dans le tas sous le programme break ( sbrk(0)) sous Unixen ...
Antti Haapala
la source
1

pointeurs (ex: char * arr, int * arr) -------> tas

Non, ils peuvent être sur la pile ou dans le segment de données. Ils peuvent pointer n'importe où.

Steve Wellens
la source
Les déclarations sur mainles variables allouées dynamiquement sont également erronées
simonc
Pas seulement sur la pile ou le segment de données. Pensez à un pointeur qui pointe vers un tableau de pointeurs. Dans ce cas, les pointeurs du tableau sont stockés sur le tas.
Sebi2020
1
  • Variables / variables automatiques ---> section stack
  • Variables allouées dynamiquement ---> section de tas
  • Variables globales initialisées -> section données
  • Variables globales non initialisées -> section data (bss)
  • Variables statiques -> section données
  • Constantes de chaîne -> section de texte / section de code
  • Fonctions -> section de texte / section de code
  • Code de texte -> section de texte / section de code
  • Registres -> registres CPU
  • Entrées de ligne de commande -> section environnement / ligne de commande
  • Variables d'environnement -> section environnement / ligne de commande
prashad
la source
Qu'est-ce que la section environnement / ligne de commande? Existent-ils sous Linux?
Haoyuan Ge
-1

Exemples exécutables minimaux Linux avec analyse de désassemblage

Puisqu'il s'agit d'un détail d'implémentation non spécifié par les normes, jetons simplement un coup d'œil à ce que le compilateur fait sur une implémentation particulière.

Dans cette réponse, je vais soit créer un lien vers des réponses spécifiques qui font l'analyse, soit fournir l'analyse directement ici, et résumer tous les résultats ici.

Tous ces éléments sont dans différentes versions d'Ubuntu / GCC, et les résultats sont probablement assez stables d'une version à l'autre, mais si nous trouvons des variantes, spécifions des versions plus précises.

Variable locale à l'intérieur d'une fonction

Que ce mainsoit ou toute autre fonction:

void f(void) {
    int my_local_var;
}

Comme indiqué à: Que signifie <value optimized out> dans gdb?

  • -O0: pile
  • -O3: enregistre s'ils ne débordent pas, empilez autrement

Pour savoir pourquoi la pile existe, voir: Quelle est la fonction des instructions push / pop utilisées sur les registres dans l'assemblage x86?

Variables globales et staticvariables de fonction

/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;

/* DATA */
int my_global_implicit_explicit_1 = 1;

void f(void) {
    /* BSS */
    static int my_static_local_var_implicit;
    static int my_static_local_var_explicit_0 = 0;

    /* DATA */
    static int my_static_local_var_explicit_1 = 1;
}

char * et char c[]

Comme indiqué à: Où les variables statiques sont-elles stockées en C et C ++?

void f(void) {
    /* RODATA / TEXT */
    char *a = "abc";

    /* Stack. */
    char b[] = "abc";
    char c[] = {'a', 'b', 'c', '\0'};
}

TODO est-ce que de très gros littéraux de chaîne seront également mis sur la pile? Ou .data? Ou la compilation échoue-t-elle?

Arguments de fonction

void f(int i, int j);

Doit passer par la convention d'appel appropriée, par exemple: https://en.wikipedia.org/wiki/X86_calling_conventions pour X86, qui spécifie soit des registres spécifiques, soit des emplacements de pile pour chaque variable.

Ensuite, comme indiqué à Que signifie <valeur optimisée sortie> dans gdb? , -O0puis insère tout dans la pile, tout en -O3essayant d'utiliser les registres autant que possible.

Cependant, si la fonction est intégrée, ils sont traités comme des locaux ordinaires.

const

Je pense que cela ne fait aucune différence car vous pouvez le typer.

Inversement, si le compilateur est capable de déterminer que certaines données ne sont jamais écrites, il pourrait en théorie les placer .rodatamême si ce n'est pas const.

Analyse TODO.

Pointeurs

Ce sont des variables (qui contiennent des adresses, qui sont des nombres), donc comme tout le reste :-)

malloc

La question n'a pas beaucoup de sens pour malloc, puisque mallocc'est une fonction, et dans:

int *i = malloc(sizeof(int));

*i est une variable qui contient une adresse, elle relève donc du cas ci-dessus.

Quant au fonctionnement de malloc en interne, lorsque vous l'appelez le noyau Linux marque certaines adresses comme inscriptibles sur ses structures de données internes, et lorsqu'elles sont touchées par le programme initialement, une erreur se produit et le noyau active les tables de pages, ce qui permet l'accès sans segfaul: comment fonctionne la pagination x86?

Notez cependant que c'est fondamentalement exactement ce que fait l' execappel système sous le capot lorsque vous essayez d'exécuter un exécutable: il marque les pages sur lesquelles il veut charger et y écrit le programme, voir aussi: Comment le noyau obtient-il un fichier binaire exécutable fonctionnant sous Linux? Sauf que cela execa des limitations supplémentaires sur l'endroit où charger (par exemple, le code n'est-il pas déplaçable ).

L'appel système exact utilisé mallocest mmapdans les implémentations modernes de 2020, et dans le passé brkétait utilisé: malloc () utilise-t-il brk () ou mmap ()?

Bibliothèques dynamiques

Fondamentalement, mmapaccédez à la mémoire: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710

variables d'environnement et maindeargv

Au-dessus de la pile initiale: /unix/75939/where-is-the-environment-string-actual-stored TODO pourquoi pas en .data?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source