web-dev-qa-db-fra.com

comment les compilateurs attribuent-ils des adresses mémoire aux variables?

J'enseigne un cours où les étudiants peuvent poser des questions sur la programmation (!): J'ai eu cette question:

Pourquoi la machine choisit-elle les variables en mémoire? Peut-on dire où stocker une variable?

Je ne sais pas trop quoi dire. Voici ma première tentative:

Le compilateur (pas la machine) choisit où stocker les variables dans l'espace d'adressage de processus automatiquement. En utilisant C, nous ne pouvons pas dire à la machine où stocker les variables.

Mais cela "automatiquement" est quelque peu anticlimatique et pose la question ... et je me rends compte que je ne sais même pas si c'est le compilateur ou le runtime ou le système d'exploitation ou qui fait la tâche. Peut-être que quelqu'un peut répondre à la question de l'étudiant mieux que moi.

24
Dervin Thunk

La réponse à cette question est assez complexe car il existe différentes approches d'allocation de mémoire en fonction de la portée, de la taille et de l'environnement de programmation variables.

Empiler les variables allouées

Généralement, local variables est placé sur la "pile". Cela signifie que le compilateur affecte un décalage au "pointeur de pile" qui peut être différent en fonction de l'appel de la fonction en cours. C'est à dire. le compilateur suppose que des emplacements de mémoire tels que Stack-Pointer + 4, Stack-Pointer + 8, etc., sont accessibles et utilisables par le programme. Sur returning de la fonction, il n’est pas garanti que les emplacements de mémoire conservent ces valeurs. 

Ceci est mappé dans des instructions d'assemblage similaires à ce qui suit. esp est le pointeur de la pile, esp + N fait référence à un emplacement mémoire relatif à esp:

mov eax, DWORD PTR SS:[esp]
mov eax, DWORD PTR SS:[esp + 4]
mov eax, DWORD PTR SS:[esp + 8]

Tas

Ensuite, il y a des variables qui sont allouées au tas. Cela signifie qu'il existe un appel de bibliothèque pour demander de la mémoire à la bibliothèque standard (alloc en C ou new en C++). Cette mémoire est réservée jusqu'à la fin de l'exécution du programme. alloc et new renvoient des pointeurs en mémoire dans une région de la mémoire appelée tas. Les fonctions d'allocation doivent s'assurer que la mémoire n'est pas réservée, ce qui peut ralentir parfois l'allocation de tas. En outre, si vous ne voulez pas manquer de mémoire, vous devriez utiliser la mémoire free (ou delete) qui n'est plus utilisée. L'allocation de tas est assez compliquée en interne car la bibliothèque standard doit garder une trace des plages utilisées et non utilisées en mémoire ainsi que des plages libérées. Par conséquent, même libérer une variable allouée au segment de mémoire peut prendre plus de temps que l’allouer. Pour plus d'informations, voir Comment malloc () est-il implémenté en interne?

Comprendre la différence entre pile et tas est fondamental pour apprendre à programmer en C et C++.

Pointeurs arbitraires

Naïvement, on pourrait supposer qu’en définissant un pointeur sur une adresse arbitraire int *a = 0x123, il devrait être possible d’adresser des emplacements arbitraires dans la mémoire de l’ordinateur. Cela n'est pas vrai, car (en fonction de la CPU et du système), les programmes sont fortement restreints lors de l'adressage en mémoire. 

Prendre conscience de la mémoire

Dans une expérience de classe guidée, il peut être intéressant d'explorer du code C simple en compilant le code source en assembleur (gcc peut le faire, par exemple). Une fonction simple telle que int foo(int a, int b) { return a+b;} devrait suffire (sans optimisations). Ensuite, voyez quelque chose comme int bar(int *a, int *b) { return (*a) + (*b);};

Lorsque vous appelez bar, allouez les paramètres une fois sur la pile, une fois par malloc.

Conclusion

Le compilateur effectue un certain positionnement et un alignement variables par rapport aux adresses de base obtenues par le programme/la bibliothèque standard au moment de l'exécution. 

Pour une compréhension approfondie des questions liées à la mémoire, voir Ulrich Drepper, "Ce que tout programmeur doit savoir sur la mémoire" http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.91.957

En dehors de C-ish Country idenote

Il existe également Garbage Collection, qui est populaire parmi de nombreux langages de script (Python, Perl, Javascript, LISP) et des environnements indépendants du périphérique (Java, C #). C'est lié à l'allocation de tas mais un peu plus compliqué.

Les variétés de langages de programmation sont uniquement basées sur des tas (python sans pile) ou entièrement sur des piles.

28
wirrbel

Je pense que la réponse à cette question commence par une compréhension de la disposition d'un programme en mémoire. Sous le système d'exploitation, la mémoire principale d'un ordinateur n'est qu'un tableau géant. Lorsque vous exécutez un programme, le système d'exploitation utilise une partie de cette mémoire et la divise en sections logiques aux fins suivantes:

  • pile: cette zone de mémoire stocke des informations sur toutes les fonctions actuellement dans la portée, y compris la fonction en cours d'exécution et tous ses ancêtres. Les informations stockées incluent les variables locales et l'adresse à laquelle retourner lorsque la fonction est terminée.

  • heap: cette zone de mémoire est utilisée lorsque vous souhaitez allouer dynamiquement du stockage. Généralement, votre variable locale contiendrait alors une adresse (c'est-à-dire un pointeur) dans le tas où vos données sont stockées et vous pourriez publier cette adresse dans d'autres parties de votre programme sans craindre que vos données ne soient écrasées lors de la suppression de la donnée courante la fonction sort du cadre.

  • data, bss, text segments: ceux-ci sortent plus ou moins du cadre de cette question particulière, mais ils stockent des éléments tels que les données globales et le programme lui-même.

J'espère que cela pourra aider. Il y a aussi beaucoup de bonnes ressources en ligne. Je viens de googler "mise en page d'un programme en mémoire" et a trouvé celui-ci: http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory

7
danben

Je voudrais ajouter quelques-uns. Pour les microprogrammes où vous connaissez la carte de mémoire et l'adresse d'exécution, et où vous compilez la source à l'aide de votre propre script de l'éditeur de liens -

Vous pouvez affecter une section personnalisée à une variable à l'aide de l'attribut section, puis attribuer une adresse particulière à la section personnalisée via le script de l'éditeur de liens. Ensuite, la variable obtiendrait une adresse fixe/assignée.

0
Rajiv Kumar Srivastav

Les variables locales dans une fonction seront généralement disposées de manière séquentielle dans le cadre de pile de la fonction.

0
SLaks