Question Comment savoir où se trouve ma valeur en mémoire?


Quand j'écris un programme et que je le raconte int c=5, il met la valeur 5 dans un peu de sa mémoire, mais comment se souvient-il de laquelle? La seule façon dont je pouvais penser serait d'avoir un peu de mémoire pour le raconter, mais il faudrait alors se rappeler où cela se trouvait, alors comment se souvient-il de tout?


32
2017-09-03 13:19


origine


Réponses:


Il y a beaucoup de bonnes réponses ici, mais elles semblent toutes manquer un point important qui, à mon avis, a été le principal objectif de la question du programme opérationnel. Je parle de langages compilés comme C ++, les langages interprétés sont beaucoup plus complexes.

Lors de la compilation de votre programme, le compilateur examine votre code pour trouver toutes les variables. Certaines variables vont être globales (ou statiques) et d'autres vont être locales. Pour les variables statiques, il leur attribue des adresses mémoire fixes. Ces adresses sont susceptibles d'être séquentielles et commencent à une valeur spécifique. En raison de la segmentation de la mémoire sur la plupart des architectures (et des mécanismes de mémoire virtuelle), chaque application peut (potentiellement) utiliser les mêmes adresses mémoire. Ainsi, si nous supposons que les programmes d’espace mémoire sont autorisés à démarrer à 0 dans notre exemple, chaque programme que vous compilerez placera la première variable globale à l’emplacement 0. Si cette variable était de 4 octets, le prochain serait à l’emplacement 4, etc. Ils ne seront pas en conflit avec d'autres programmes en cours d'exécution sur votre système, car ils sont en fait mappés à une section séquentielle arbitraire de la mémoire au moment de l'exécution. C'est pourquoi il peut attribuer une adresse fixe à la compilation sans se soucier de frapper d'autres programmes.

Pour les variables locales, au lieu de se voir attribuer une adresse fixe, une adresse fixe leur est attribuée par rapport au pointeur de pile (qui est généralement un registre). Lorsqu'une fonction est appelée et alloue des variables sur la pile, le pointeur de pile est simplement déplacé par le nombre d'octets requis, créant un espace entre les octets utilisés dans la pile. Toutes les variables locales sont affectées à des décalages fixes du pointeur de pile qui les placent dans cet espace. Chaque fois qu'une variable locale est utilisée, l'adresse de la mémoire réelle est calculée en ajoutant le pointeur de la pile et le décalage (en négligeant les valeurs de mise en cache dans les registres). Lorsque la fonction retourne, le pointeur de la pile est réinitialisé à la manière dont il se trouvait avant l'appel de la fonction. Ainsi, l'intégralité du cadre de la pile, y compris les variables locales, peut être écrasée par l'appel de fonction suivant.


9
2017-09-03 16:47



Votre code est compilé avant exécution, à cette étape, votre variable sera remplacée par la référence réelle de l'espace où la valeur sera stockée.

C'est du moins le principe général. En réalité, ce sera beaucoup plus compliqué, mais toujours la même idée de base.


13
2017-09-03 13:21



lire la variable (programmation) - Allocation de mémoire:
http://en.wikipedia.org/wiki/Variable_(programming)#Memory_allocation

voici le texte du lien (si vous ne voulez pas y aller, mais il vous manque tous les liens dans le texte):

Les spécificités de l'allocation des variables   et la représentation de leurs valeurs   varier considérablement, à la fois parmi les programmes   langues et parmi les implémentations de   une langue donnée. Beaucoup de langue   les implémentations allouent de l'espace pour   variables locales, dont l'étendue dure   pour un seul appel de fonction sur l'appel   pile, et dont la mémoire est   automatiquement récupéré lorsque le   fonction retourne. (Plus généralement, dans   liaison de nom, nom d'une variable   est lié à l'adresse de certains   bloc particulier (séquence contiguë)   d'octets en mémoire et opérations sur   la variable manipule ce bloc.   Le référencement est plus commun pour   les variables dont les valeurs sont grandes ou   tailles inconnues lorsque le code est   compilé. Ces variables font référence au   emplacement de la valeur au lieu de la   stocker la valeur elle-même, qui est   alloué à partir d'un pool de mémoire appelé   le tas.

Les variables liées ont des valeurs. Une valeur,   cependant, est une abstraction, une idée;   dans la mise en œuvre, une valeur est   représenté par un objet de données, qui   est stocké quelque part dans l'ordinateur   Mémoire. Le programme ou le runtime   environnement, doit mettre de côté la mémoire pour   chaque objet de données et, comme la mémoire est   fini, assurez-vous que cette mémoire est   cédé pour la réutilisation lorsque l'objet est   plus besoin de représenter certains   valeur de la variable.

Les objets alloués à partir du tas doivent   être récupéré-surtout quand le   les objets ne sont plus nécessaires. Dans un   langage récupéré (comme   C #, Java et Lisp), le runtime   environnement récupère automatiquement   objets lorsque les variables existantes ne peuvent pas   plus longtemps s'y référer. Dans   langues non collectées, telles que   comme C, le programme (et le programmeur)   doit explicitement allouer de la mémoire, et   puis plus tard le libérer, pour réclamer son   Mémoire. Ne pas le faire conduit à   fuites de mémoire, dans lequel le tas est   épuisé que le programme fonctionne, risquant   échec éventuel de l'épuisement   mémoire disponible.

Quand une variable fait référence à une donnée   structure créée dynamiquement, certains de   ses composants ne peuvent être qu'indirectement   accessible via la variable. Dans une telle   circonstances, les éboueurs (ou   caractéristiques de programme analogues dans   des langues qui manquent d'ordures   les collectionneurs) doivent traiter un cas   où seule une partie de la mémoire   accessible de la variable doit   être récupéré


6
2017-09-03 13:23



Il y a une danse en plusieurs étapes qui tourne c = 5 dans les instructions de la machine pour mettre à jour un emplacement en mémoire.

  1. Le compilateur génère du code en deux parties. Il y a la partie instruction (charger un registre avec l'adresse de C; charger un registre avec le littéral 5; store). Et il y a une partie d'allocation de données (laissez 4 octets d'espace à l'offset 0 pour une variable appelée "C").

  2. Un "chargeur de liens" doit mettre ces éléments en mémoire de manière à ce que le système d'exploitation puisse les exécuter. Le chargeur demande de la mémoire et le système d'exploitation alloue des blocs de mémoire virtuelle. Le système d'exploitation mappe également la mémoire virtuelle sur la mémoire physique via un ensemble de mécanismes de gestion sans rapport.

  3. Le chargeur place la page de données dans un endroit et la partie instruction dans un autre endroit. Notez que les instructions utilisent des adresses relatives (un décalage de 0 dans la page de données). Le chargeur fournit l'emplacement réel de la page de données afin que les instructions puissent résoudre l'adresse réelle.

  4. Lorsque l'instruction "store" réelle est exécutée, le système d'exploitation doit voir si la page de données référencée est réellement dans la mémoire physique. Il peut être dans le fichier d'échange et doit être chargé dans la mémoire physique. L'adresse virtuelle utilisée est traduite en une adresse physique des emplacements de mémoire.


6
2017-09-03 13:39



C'est intégré dans le programme.

Fondamentalement, lorsqu'un programme est compilé en langage machine, il devient une série d'instructions. Certaines instructions ont des adresses de mémoire intégrées, et c'est la "fin de la chaîne", pour ainsi dire. Le compilateur décide où chaque variable sera et graver ces informations dans le fichier exécutable. (Rappelez-vous que le compilateur est un programme différent du programme que vous écrivez; concentrez-vous simplement sur le fonctionnement de votre propre programme pour le moment.)

Par exemple,

ADD [1A56], 15

peut ajouter 15 à la valeur à l'emplacement 1A56. (Cette instruction serait codée en utilisant un code que le processeur comprend, mais je ne l'expliquerai pas.)

Maintenant, d'autres instructions vous permettent d'utiliser une adresse mémoire "variable" - une adresse mémoire qui a elle-même été chargée depuis un emplacement donné. C'est la base des pointeurs en C. Vous ne pouvez certainement pas en avoir une chaîne infinie, sinon vous risquez de manquer de mémoire.

J'espère que ça clarifie les choses.


4
2017-09-03 13:35



Je vais formuler ma réponse dans une terminologie très simple. S'il vous plaît ne soyez pas insulté, je ne suis pas sûr de savoir comment vous êtes déjà et souhaitez fournir une réponse acceptable à quelqu'un qui pourrait être un débutant total.

Vous n'êtes pas si loin dans votre hypothèse. Le programme par lequel vous exécutez votre code, généralement appelé compilateur (ou interpréteur, selon la langue), garde la trace de toutes les variables que vous utilisez. Vous pouvez considérer vos variables comme une série de bacs et les différentes données sont conservées dans ces bacs. Les bacs ont des étiquettes, et lorsque vous créez votre code source dans un programme que vous pouvez exécuter, toutes les étiquettes sont reportées. Le compilateur s'en charge pour vous, donc lorsque vous exécutez le programme, les choses appropriées sont extraites de leur répertoire respectif.

Les variables que vous utilisez ne sont qu'une autre couche d'étiquettes. Cela rend les choses plus faciles à suivre. La manière dont les variables sont stockées en interne peut comporter des étiquettes très complexes ou cryptées, mais tout ce dont vous avez besoin est de savoir comment vous vous y référez dans votre code. Restez cohérent, utilisez de bons noms de variables et gardez une trace de ce que vous faites avec vos variables et le compilateur / interprète se charge de gérer les tâches de bas niveau associées. Il s’agit d’un cas très simple d’utilisation variable de la mémoire.


3
2017-09-03 13:51



Vous devriez étudier les pointeurs.

http://home.netcom.com/~tjensen/ptr/ch1x.htm


1
2017-09-03 13:21



Réduite au nu, une recherche de variable se réduit à une adresse qui est un décalage connu statiquement d'un pointeur de base contenu dans un registre (le pointeur de pile), ou une adresse constante (variable globale).

Dans un langage interprété, un registre est souvent réservé pour contenir un pointeur sur une structure de données («environnement») qui associe des noms de variable à leurs valeurs actuelles.


1
2017-09-03 13:39



En fin de compte, les ordinateurs ne font que comprendre, ce que nous résumons facilement en binaire. Ce langage est le niveau le plus bas et est appelé langage machine. Je ne suis pas sûr que ce soit du folklore - mais certains programmeurs avaient l'habitude (ou peut-être le font encore) de programmer directement en langage machine. Taper ou lire en binaire serait très compliqué, ce qui explique pourquoi l'hexadécimal est souvent utilisé pour abréger le binaire réel.

Comme la plupart d'entre nous ne sont pas des savants, le langage machine est extrait dans le langage d'assemblage. Assemply est un langage très primitif qui contrôle directement la mémoire. Il y a un nombre très limité de commandes (push / pop / add / goto), mais celles-ci accomplissent finalement tout ce qui est programmé. Différentes architectures de machine ont différentes versions d'assemblage, mais l'essentiel est qu'il y a quelques dizaines de registres de mémoire clés (physiquement dans le CPU) - dans une architecture x86, ils sont EAX, EBX, ECX, EDX, ... des pointeurs que le processeur utilise pour déterminer ce qu’il faut faire ensuite. Le processeur ne peut faire qu'une chose à la fois et il utilise ces registres pour savoir quoi faire ensuite. Les ordinateurs semblent être capables de faire beaucoup de choses en même temps, car le processeur peut traiter ces instructions très rapidement - (millions / milliards d'instructions par seconde). Bien sûr, les processeurs multi-core compliquent les choses, mais n'allons pas là-bas ...

Comme la plupart d'entre nous ne sont pas assez intelligents ou précis pour programmer en assembleur où vous pouvez facilement planter le système, l'assemblage est ensuite résumé dans un langage de troisième génération (3GL) - il s'agit de votre C / C ++ / C # / Java etc. vous dites à l'une de ces langues de mettre la valeur entière 5 dans une variable, vos instructions sont stockées dans du texte; l'assembleur compile votre texte dans un fichier d'assemblage (exécutable); Lorsque le programme est exécuté, le processeur et ses instructions sont mis en file d'attente par le processeur, quand il est temps de montrer pour cette ligne de code spécifique, il est lu dans le registre du processeur et traité.

Les commentaires «pas assez intelligents» sur les langues sont un peu irrésistibles. Théoriquement, plus vous vous éloignez des zéros et des mots simples, plus vous devez pouvoir produire du code rapidement et efficacement.


1
2017-09-03 15:33



Il y a un défaut important que quelques personnes font, à savoir que toutes les variables sont stockées en mémoire. Eh bien, à moins que vous comptiez les registres du processeur en tant que mémoire, cela ne sera pas tout à fait correct. Certains compilateurs vont optimiser le code généré et s'ils peuvent conserver une variable stockée dans un registre, certains compilateurs s'en serviront! Alors, bien sûr, il y a la question complexe du tas et de la mémoire de pile. Les variables locales peuvent être localisées dans les deux! L'emplacement préféré serait dans la pile, à laquelle on accède beaucoup plus souvent que le tas. C'est le cas pour presque toutes les variables locales. Les variables globales font souvent partie du segment de données du fichier exécutable final et ont tendance à faire partie du segment de mémoire, bien que vous ne puissiez pas libérer ces zones de mémoire globale. Mais le tas est souvent utilisé pour les attributions à la volée de nouveaux blocs de mémoire, en allouermémorisant pour eux.

Mais avec les variables globales, le code saura exactement où elles se trouvent et inscrira donc leur emplacement exact dans le code. (Eh bien, leur emplacement depuis le début du segment de données de toute façon.) Les variables de registre sont situées dans le CPU et le compilateur sait exactement quel registre, qui vient également d'être transmis au code. Les variables de pile sont situées à un décalage du pointeur de pile actuel. Ce pointeur de pile augmentera et diminuera tout le temps, en fonction du nombre de niveaux de procédures appelant d'autres procédures. Seules les valeurs de tas sont complexes. Lorsque l'application a besoin de stocker des données sur le tas, elle a besoin d'une seconde variable pour stocker l'adresse, sinon elle pourrait perdre la trace. Cette seconde variable est appelée un pointeur et se trouve en tant que données globales ou en tant que partie de la pile. (Ou, dans de rares occasions, dans les registres de la CPU.)

Oh, c'est même un peu plus complexe que ça, mais je peux déjà voir des yeux qui tournent à cause de cette surcharge d'information. :-)


1
2017-09-03 15:59