web-dev-qa-db-fra.com

Comment les noms de variables sont-ils stockés en mémoire en C?

En C, disons que vous avez une variable appelée variable_name. Disons qu'il est situé à 0xaaaaaaaa, et à cette adresse mémoire, vous avez le nombre entier 123. En d'autres termes, variable_name contient 123.

Je recherche des éclaircissements sur le libellé "variable_name est situé à 0xaaaaaaaa ". Comment le compilateur reconnaît-il que la chaîne" nom_variable "est associée à cette adresse mémoire particulière? La chaîne" nom_variable "est-elle stockée quelque part en mémoire? Le compilateur remplace-t-il simplement variable_name pour 0xaaaaaaaa chaque fois qu'il le voit, et si oui, ne devrait-il pas utiliser la mémoire pour effectuer cette substitution?

44
Tyler

Les noms de variables n'existent plus après l'exécution du compilateur (sauf cas particuliers comme les globaux exportés dans les bibliothèques partagées ou les symboles de débogage). L'ensemble de l'acte de compilation est destiné à prendre ces noms symboliques et algorithmes représentés par votre code source et à les transformer en instructions machine natives. Alors oui, si vous avez un variable_name Global et que le compilateur et l'éditeur de liens décident de le mettre à 0xaaaaaaaa, Alors où qu'il soit utilisé dans le code, il sera simplement accessible via cette adresse.

Donc, pour répondre à vos questions littérales:

Comment le compilateur reconnaît-il que la chaîne "nom_variable" est associée à cette adresse mémoire particulière?

La chaîne d'outils (compilateur et éditeur de liens) fonctionne ensemble pour attribuer un emplacement mémoire à la variable. C'est le travail du compilateur de garder une trace de toutes les références, et l'éditeur de liens met les bonnes adresses plus tard.

La chaîne "variable_name" Est-elle stockée quelque part en mémoire?

Uniquement lorsque le compilateur est en cours d'exécution.

Le compilateur substitue-t-il simplement variable_name À 0xaaaaaaaa Chaque fois qu'il le voit, et si oui, n'aurait-il pas besoin d'utiliser de la mémoire pour effectuer cette substitution?

Oui, c'est à peu près ce qui se passe, sauf que c'est un travail en deux étapes avec l'éditeur de liens. Et oui, il utilise de la mémoire, mais c'est la mémoire du compilateur, rien au moment de l'exécution pour votre programme.

Un exemple pourrait vous aider à comprendre. Essayons ce programme:

int x = 12;

int main(void)
{
    return x;
}

Assez simple, non? D'ACCORD. Prenons ce programme, compilons-le et examinons le démontage:

$ cc -Wall -Werror -Wextra -O3    example.c   -o example
$ otool -tV example
example:
(__TEXT,__text) section
_main:
0000000100000f60    pushq   %rbp
0000000100000f61    movq    %rsp,%rbp
0000000100000f64    movl    0x00000096(%rip),%eax
0000000100000f6a    popq    %rbp
0000000100000f6b    ret

Vous voyez cette ligne movl? Il s'agit de saisir la variable globale (d'une manière relative au pointeur d'instruction, dans ce cas). Plus aucune mention de x.

Maintenant, faisons un peu plus compliqué et ajoutons une variable locale:

int x = 12;

int main(void)
{  
    volatile int y = 4;
    return x + y;
}

Le démontage de ce programme est:

(__TEXT,__text) section
_main:
0000000100000f60    pushq   %rbp
0000000100000f61    movq    %rsp,%rbp
0000000100000f64    movl    $0x00000004,0xfc(%rbp)
0000000100000f6b    movl    0x0000008f(%rip),%eax
0000000100000f71    addl    0xfc(%rbp),%eax
0000000100000f74    popq    %rbp
0000000100000f75    ret

Il existe maintenant deux instructions movl et une instruction addl. Vous pouvez voir que le premier movl initialise y, dont il est décidé qu'il se trouvera sur la pile (pointeur de base - 4). Ensuite, le movl suivant obtient le x global dans un registre eax, et le addl ajoute y à cette valeur. Mais comme vous pouvez le voir, les chaînes littérales x et y n'existent plus. Ils étaient pratiques pour vous, le programmeur, mais l'ordinateur ne se soucie certainement pas d'eux au moment de l'exécution.

77
Carl Norum

Un compilateur C crée d'abord une table de symboles, qui stocke la relation entre le nom de la variable et son emplacement en mémoire. Lors de la compilation, il utilise ce tableau pour remplacer toutes les instances de la variable par un emplacement mémoire spécifique, comme d'autres l'ont indiqué. Vous pouvez en trouver beaucoup plus sur la page Wikipedia.

10
MichaelThiessen

Toutes les variables sont remplacées par le compilateur. D'abord, ils sont remplacés par des références, puis l'éditeur de liens place des adresses au lieu de références.

En d'autres termes. Les noms de variables ne sont plus disponibles dès que le compilateur est passé par

6
junix

C'est ce qu'on appelle un détail d'implémentation. Bien que ce que vous décrivez soit le cas dans tous les compilateurs que j'ai jamais utilisés, ce n'est pas obligatoire. Un compilateur C pourrait mettre chaque variable dans une table de hachage et les rechercher lors de l'exécution (ou quelque chose comme ça) et en fait, les premiers interprètes JavaScript ont fait exactement cela (maintenant, ils font une compilation Just-In-TIme qui aboutit à quelque chose de beaucoup plus brut).

Spécifiquement pour les compilateurs courants comme VC++, GCC et LLVM: le compilateur affectera généralement une variable à un emplacement en mémoire. Les variables de portée globale ou statique obtiennent une adresse fixe qui ne change pas pendant l'exécution du programme, tandis que les variables au sein d'une fonction obtiennent une adresse pile, c'est-à-dire une adresse relative au pointeur de pile actuel, qui change chaque fois qu'une fonction est appelée. (Il s'agit d'une simplification excessive.) Les adresses de pile deviennent invalides dès le retour de la fonction, mais ont l'avantage de n'avoir effectivement aucun temps système à utiliser.

Une fois qu'une variable a une adresse qui lui est affectée, il n'y a plus besoin de son nom, elle est donc supprimée. Selon le type de nom, le nom peut être ignoré lors du prétraitement (pour les noms de macro), de la compilation (pour les variables/fonctions statiques et locales) et lors de la liaison (pour les variables/fonctions globales). Si un symbole est exporté ( rendu visible aux autres programmes afin qu'ils puissent y accéder), le nom restera généralement quelque part dans une "table des symboles" qui fait occupe une quantité insignifiante de mémoire et d'espace disque.

6
Jonathan Grynspan

Le compilateur remplace-t-il simplement nom_variable par 0xaaaaaaaa chaque fois qu'il le voit

Oui.

et si oui, ne devrait-il pas utiliser la mémoire pour effectuer cette substitution?

Oui. Mais c'est le compilateur, après avoir compilé votre code, pourquoi vous souciez-vous de la mémoire?

4
vanza