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.
c
memory
memory-management
types
starkk92
la source
la source
main
est juste une autre fonction. Les variables vont sur la pile à moins quemalloc
'd comme ailleurs.Réponses:
Vous en avez raison, mais celui qui a écrit les questions vous a trompé sur au moins une question:
main
fonction ----->tass'empilent également (l'enseignant essayait de vous tromper)char *arr
,int *arr
) ------->tasdonnées ou pile, en fonction du contexte. C vous permet de déclarer un global ou unstatic
pointeur, auquel cas le pointeur lui-même se retrouverait dans le segment de données.malloc
,calloc
,realloc
) -------->piletasIl est à noter que «pile» est officiellement appelée «classe de stockage automatique».
la source
alloca
similaire à l'malloc
allocation de pile, mais qui le fait.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:
5 segments de mémoire en C:
1. Segment de code
printf("Hello, world")
chaîne "Hello, world" est créée dans le segment code / texte. Vous pouvez vérifier cela à l'aide de lasize
commande dans le système d'exploitation Linux.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é
int globalVar;
ou variable locale statiquestatic int localStatic;
sera stockée dans le segment de données non initialisé.0
ouNULL
alors elle irait toujours au segment de données non initialisé ou bss.3. Segment de données initialisé
int globalVar = 1;
ou une variable locale statiquestatic int localStatic = 1;
sera stockée dans un segment de données initialisé.4. Segment de pile
5. Segment de tas
malloc
,calloc
ourealloc
des méthodes.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 laptr
variable. 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.la source
Correction de vos mauvaises phrases
variables constantes locales -----> pile
variable constante globale initialisée -----> segment de données
variable constante globale non initialisée -----> bss
variables déclarées et définies dans la fonction principale -----> pile
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
malloc
oucalloc
) dynamiquement et que vous créez ensuite une variable de pointeur pour la pointer. Maintenant que lesn
octets 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 desn
octets du bloc de mémoire.Remarque: les variables de pointeur peuvent pointer la mémoire de n'importe quel segment.
espace alloué dynamiquement (en utilisant malloc, calloc) --------> tas
la source
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.
la source
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.
la source
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:
42
dans le code d'assemblage généré mais aucun signe de404
.const
ouconst
n'a pas besoin d'être en mémoire. Exemple - le compilateur peut prouver quefoo
c'est effectivement le casconst
et intègre son utilisation dans le code.bar
a 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.malloc
n'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 àmalloc
et que la valeur 42 n'est jamais stockée en mémoire, elle est conservée dans un registre!malloc
et la référence est perdue sans désallouer l'objet avecfree
besoin de ne pas fuir de mémoire ...malloc
n'a pas besoin d'être dans le tas sous le programme break (sbrk(0)
) sous Unixen ...la source
Non, ils peuvent être sur la pile ou dans le segment de données. Ils peuvent pointer n'importe où.
la source
main
les variables allouées dynamiquement sont également erronéesla source
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
main
soit ou toute autre fonction:Comme indiqué à: Que signifie <value optimized out> dans gdb?
-O0
: pile-O3
: enregistre s'ils ne débordent pas, empilez autrementPour 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
static
variables de fonction0
ou non initialisé (et donc implicitement initialisé à0
):.bss
section, voir aussi: Pourquoi le segment .bss est-il requis?.data
sectionchar *
etchar c[]
Comme indiqué à: Où les variables statiques sont-elles stockées en C et C ++?
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
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? ,
-O0
puis insère tout dans la pile, tout en-O3
essayant 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
.rodata
mê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
, puisquemalloc
c'est une fonction, et dans:*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'
exec
appel 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 celaexec
a 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é
malloc
estmmap
dans les implémentations modernes de 2020, et dans le passébrk
était utilisé: malloc () utilise-t-il brk () ou mmap ()?Bibliothèques dynamiques
Fondamentalement,
mmap
accédez à la mémoire: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710variables d'environnement et
main
deargv
Au-dessus de la pile initiale: /unix/75939/where-is-the-environment-string-actual-stored TODO pourquoi pas en .data?
la source