1
votes

Stocker l'adresse d'une variable

Je suis nouveau dans les pointeurs en langage C. Je comprends qu'un pointeur stocke l'adresse de la mémoire et non la valeur.

Si j'initialise une variable régulière avec une valeur, cette valeur est stockée dans ma mémoire. Cependant, les systèmes doivent stocker où ma valeur est, non? Donc, le système n'a-t-il pas également un pointeur vers cette variable?

int b = 3;

Si j'accède à b , le système doit connaître l'adresse où le "3" peut être trouvé, non? Ou est-ce que je me trompe?

Je sais que les variables sont sur la pile et les pointeurs peuvent pointer vers le tas / pile.


6 commentaires

"Les pointeurs pointent vers le tas" - C'est faux. Les pointeurs pointent vers la mémoire, qui peut être sur la pile ou sur le tas. Par exemple. int b = 3; int * ptr = & b; . Maintenant ptr pointe sur la pile, puisque b est sur la pile.


Vous avez tout à fait raison. Merci d'avoir corrigé. @rtoijala


Le système "n'a pas de pointeur vers cette variable" en tant que tel. Le compilateur sait où il a placé la variable et peut le coder en dur lors de l'écriture du code machine qui y accède. Il n'y a pas nécessairement d'emplacement en mémoire qui indique un pointeur vers la variable.


@rtoijala Totalement faux. Une déclaration int x; en dehors de toute fonction place cette variable dans la portée globale, qui n'est pas le tas.


@TanveerBadar Aah, vous avez raison. Pardon. J'ai utilisé le "tas" comme raccourci pour "pas de pile, pas de durée de stockage automatique". Ce n'est techniquement pas correct. Je n'étais pas sûr que l'introduction des détails techniques entre la portée globale et le tas était une bonne idée quand OP ne comprenait pas encore stack vs non-stack.


@rtoijala Introduire la mauvaise terminologie serait encore plus nuisible que de simplifier les choses pour l'explication.


4 Réponses :


2
votes

C'est le travail du compilateur de déterminer où stocker une variable. Une variable est associée à plusieurs éléments:

  • emplacement mémoire
  • type de données
  • durée de vie
  • champ d'application

Un compilateur gère implicitement (la plupart du temps) le premier et les deux derniers. Ainsi, lorsque vous déclarez une variable à l'intérieur d'une fonction, dites int x; le compilateur allouera de l'espace sur la pile et se souviendra du décalage d'ESP pour cette variable. La durée de vie est le corps de la fonction et se termine au retour de la fonction.

Le mappage n'est pas des pointeurs, mais oui, le système sait en raison de la nature du code généré où réside une variable locale.

Un raisonnement similaire s'applique aux variables globales, le compilateur gère leur allocation / stockage.

P.S .: ci-dessus suppose un jeu d'instructions x86 / x64. Suppose également qu'il n'y ait aucune optimisation, des éléments pourraient résider dans des registres, etc.

P.P.S .: Pensez à un pointeur comme une autre variable entière, mais cette fois la valeur qu'il contient a une signification particulière. C'est un décalage dans la mémoire, l'adresse. Encore une fois, je simplifie des choses comme les modes d'adressage. Vous finirez par lire à propos de ces choses.


1 commentaires

Merci Monsieur! Excellente explication et simple à comprendre !!



3
votes

Disons que le programme est:

+-------------------------------------- +
| function | variable | value | address |
+---------------------------------------+
| main()   |    i     |  8    | 0Xabc   |
|          |    j     |  ?    | 0Xbca   |      
+---------------------------------------+

Lorsque la fonction main () est appelée, un espace de pile comme celui-ci est alloué pour une opération ultérieure:

XXX

L'espace de la pile est alloué mais le compilateur connaît juste le décalage et la base du cadre de la pile et avec l'aide de ce compilateur de décalage localise la variable locale. Il n'y a pas de pointeur impliqué pour enregistrer l'adresse pour localiser la variable.


1 commentaires

Merci pour cette excellente réponse et visualisation.



1
votes

Les adresses mémoire sont des éléments constitutifs. Ils peuvent être utilisés de plusieurs manières:

  • Par le compilateur, en interne, afin d'implémenter des instructions telles que int b = 3; .
  • Par le programmeur, afin d'accéder au tas.
  • Par le programmeur, afin d'implémenter l'indirection.

Compilateur

Compte tenu des instructions

typedef struct { int x; int y; } example_t;
example_t stack_e;
example_t heap_e = malloc(sizeof(example_t));
example_t *e;
if (some_global_var == 1)
    e = heap_e;
else
    e = &stack_e;
// do something with e

Le compilateur doit référencer l'adresse de b dans afin de le donner à printf().

Je ne répéterai pas les autres réponses ici, ils expliquent bien comment cela fonctionne.

Heap Accès

Contrairement à d'autres parties de l'exécution d'un programme, le tas n'est accessible que via des pointeurs:

int *p = malloc(ints_to_alloc * sizeof(int));

Le seul moyen d'obtenir une quantité inconnue de int s qui peuvent vivre à travers toute exécution de fonction particulière est de les allouer sur le tas.

Indirection

Les pointeurs peuvent également être utilisés pour implémenter l'indirection, peu importe où :

int b = 3;
...
printf("%d\n", b);


1 commentaires

Merci pour votre excellente réponse à! Vraiment apprécié



1
votes

Lorsque le compilateur écrit des instructions pour un programme, il n'a pas besoin de connaître «l'adresse» d'un objet, pour deux raisons:

  • Les architectures informatiques typiques ont des formes d'instructions qui accèdent à un objet à partir d'une adresse dans un registre de processeur plus ou moins un montant fixe.
  • Le compilateur prépare des instructions avec des adresses partielles et l'adresse complète est renseignée par l'éditeur de liens lors de la construction de l'exécutable final.

Pour la pile en particulier, il existe un registre appelé le pointeur de pile. Lorsque le chargeur de programme charge le programme à exécuter, il définit le pointeur de pile. Ensuite, les objets de la pile peuvent être référencés via des décalages constants à partir du pointeur de pile. Lorsque le compilateur voit des définitions dans une fonction telle que int q ou double x , il planifie un espace sur la pile où celles-ci seront stockées et écrit des instructions qui s'y réfèrent comme «8 octets au-dessus de l'endroit où pointe le pointeur de pile», par exemple. (Il peut également y avoir un pointeur de cadre qui pointe vers une partie de la pile, de sorte que les objets peuvent être référencés par rapport au pointeur de cadre au lieu du pointeur de pile.)

Lorsque votre programme prend l'adresse d'un objet sur la pile, tel que & x , le programme le calculera en calculant le contenu du pointeur de pile plus le décalage vers x .

Pour les objets avec une durée de stockage statique, comme un int z; défini en dehors de toute fonction, le compilateur prévoit de l'espace pour eux dans une section de données de la mémoire. Lorsque le compilateur écrit le fichier objet, il inclut des descriptions des symboles souhaités dans la section de données, y compris l'emplacement de ces symboles dans la section de données. Ensuite, dans les instructions qu'il écrit, il place des informations d'espace réservé indiquant que "cette instruction fait référence au symbole z de la section de données." Lorsque l'éditeur de liens relie ce programme ou que le chargeur de programme le charge, il décide de l'emplacement de la section de données en mémoire. Ensuite, il ajuste ces instructions pour y mettre la bonne adresse.

Parfois, les instructions contiennent des adresses complètes et l'éditeur de liens ou le chargeur les ajuste lorsqu'il décide de l'adresse finale. Parfois, les instructions font référence à des objets via des décalages à partir de registres, de la même manière que la pile. Dans ce cas, le chargeur définira un registre pour qu'il pointe vers le début de la section de données ou un autre point de référence, et les instructions feront référence à des objets statiques comme z utilisant ce registre plus un décalage.

Il existe également d'autres schémas disponibles, comme demander au programme de rechercher des adresses dans une table. Mais, dans l'ensemble, le compilateur n'a pas besoin de pointeurs supplémentaires pour pointer vers des objets. Il a des moyens de faire référence aux objets directement dans les instructions, sans toujours créer de pointeurs supplémentaires.


1 commentaires

Excellente explication du fonctionnement de la pile. M'a beaucoup aidé, apprécié !!