Comment les compilateurs atsortingbuent-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.

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

Emstackr les variables allouées

Généralement, local variables sont placées dans la “stack”. Cela signifie que le compilateur affecte un décalage au “pointeur de stack” qui peut être différent en fonction de l’appel de la fonction en cours. C’est-à-dire que 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. Au return 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 stack, 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 à la 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 de new pointeurs de retour à la mémoire dans une région de la mémoire appelée le 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 devez free (ou delete ) la mémoire 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 stack 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). Puis, voyez quelque chose comme int bar(int *a, int *b) { return (*a) + (*b);} ;

Lorsque vous appelez bar, allouez les parameters une fois sur la stack, 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 stack) ou entièrement sur des stacks.

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:

  • stack: cette zone de mémoire stocke des informations sur toutes les fonctions actuellement dans la scope, 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 de manière dynamic 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 en cours. la fonction sort du cadre.

  • data, bss, text segments: ils sont plus ou moins en dehors du champ 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 “la 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

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

Je voudrais append quelques-uns. Pour les microprogrammes où vous connaissez la carte mémoire et l’adresse d’exécution, et où vous comstackz 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’atsortingbut section, puis atsortingbuer 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.