Que signifie vraiment «Mémoire allouée au moment de la compilation»?

159

Dans les langages de programmation comme C et C ++, les gens se réfèrent souvent à l'allocation de mémoire statique et dynamique. Je comprends le concept mais l'expression "Toute la mémoire a été allouée (réservée) pendant la compilation" me confond toujours.

La compilation, si je comprends bien, convertit le code C / C ++ de haut niveau en langage machine et génère un fichier exécutable. Comment la mémoire est-elle «allouée» dans un fichier compilé? La mémoire n'est-elle pas toujours allouée dans la RAM avec tous les éléments de gestion de la mémoire virtuelle?

L'allocation de mémoire n'est-elle pas par définition un concept d'exécution?

Si je crée une variable allouée statiquement de 1 Ko dans mon code C / C ++, cela augmentera-t-il la taille de l'exécutable du même montant?

C'est l'une des pages où la phrase est utilisée sous la rubrique "allocation statique".

Retour aux sources: allocation de mémoire, une promenade dans l'histoire

Talha Sayed
la source
le code et les données sont totalement séparés dans la plupart des architectures modernes. alors que les fichiers source contiennent les deux données de code au même endroit, le bac ne contient que des références aux données. Cela signifie que les données statiques dans la source ne sont résolues qu'en tant que références.
Cholthi Paul Ttiopic

Réponses:

184

La mémoire allouée au moment de la compilation signifie que le compilateur résout au moment de la compilation où certaines choses seront allouées à l'intérieur de la carte mémoire du processus.

Par exemple, considérons un tableau global:

int array[100];

Le compilateur connaît au moment de la compilation la taille du tableau et la taille de an int, donc il connaît la taille entière du tableau au moment de la compilation. De plus, une variable globale a une durée de stockage statique par défaut: elle est allouée dans la zone mémoire statique de l'espace mémoire du processus (section .data / .bss). Compte tenu de ces informations, le compilateur décide lors de la compilation à quelle adresse de cette zone de mémoire statique le tableau sera .

Bien sûr, les adresses mémoire sont des adresses virtuelles. Le programme suppose qu'il dispose de son propre espace mémoire entier (de 0x00000000 à 0xFFFFFFFF par exemple). C'est pourquoi le compilateur pourrait faire des hypothèses telles que "D'accord, le tableau sera à l'adresse 0x00A33211". Au moment de l'exécution, ces adresses sont traduites en adresses réelles / matérielles par la MMU et le système d'exploitation.

Les éléments de stockage statique à valeur initialisée sont un peu différents. Par exemple:

int array[] = { 1 , 2 , 3 , 4 };

Dans notre premier exemple, le compilateur a uniquement décidé où le tableau sera alloué, en stockant ces informations dans l'exécutable.
Dans le cas de choses initialisées par valeur, le compilateur injecte également la valeur initiale du tableau dans l'exécutable et ajoute du code qui indique au chargeur de programme qu'après l'allocation du tableau au démarrage du programme, le tableau doit être rempli avec ces valeurs.

Voici deux exemples d'assembly généré par le compilateur (GCC4.8.1 avec cible x86):

Code C ++:

int a[4];
int b[] = { 1 , 2 , 3 , 4 };

int main()
{}

Assemblage de sortie:

a:
    .zero   16
b:
    .long   1
    .long   2
    .long   3
    .long   4
main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

Comme vous pouvez le voir, les valeurs sont directement injectées dans l'assemblage. Dans le tableau a, le compilateur génère une initialisation à zéro de 16 octets, car la norme dit que les éléments stockés statiques doivent être initialisés à zéro par défaut:

8.5.9 (Initialiseurs) [Note]:
Chaque objet de durée de stockage statique est initialisé à zéro au démarrage du programme avant toute autre initialisation. Dans certains cas, une initialisation supplémentaire est effectuée ultérieurement.

Je suggère toujours aux gens de désassembler leur code pour voir ce que le compilateur fait vraiment avec le code C ++. Cela s'applique des classes / durée de stockage (comme cette question) aux optimisations avancées du compilateur. Vous pouvez demander à votre compilateur de générer l'assembly, mais il existe de merveilleux outils pour le faire sur Internet de manière conviviale. Mon préféré est GCC Explorer .

Manu343726
la source
2
Merci. Cela clarifie beaucoup. Ainsi, le compilateur produit quelque chose d'équivalent à "réserve de mémoire de 0xABC à 0xXYZ pour le tableau de variables [] etc." et puis le chargeur l'utilise pour vraiment l'allouer juste avant d'exécuter le programme?
Talha Sayed le
1
@Talha a dit exactement. Voir la modification pour regarder l'exemple
Manu343726
2
@Secko J'ai simplifié les choses. Ce n'est qu'une mention sur le programme fonctionne via la mémoire virtuelle, mais comme la question ne concerne pas la mémoire virtuelle, je n'ai pas étendu le sujet. Je signalais seulement que le compilateur peut faire des hypothèses sur les adresses mémoire au moment de la compilation, grâce à la mémoire virtuelle.
Manu343726
2
@Secko oui. mmm "généré" est un meilleur terme je pense.
Manu343726
2
"Son alloué dans la zone de mémoire statique de l'espace mémoire de processus" Lecture qui a alloué certaines zones mammaires statiques dans mon espace mémoire de processus.
Radiodef
27

La mémoire allouée au moment de la compilation signifie simplement qu'il n'y aura plus d'allocation au moment de l'exécution - aucun appel à malloc, nouvelle ou autre méthode d'allocation dynamique. Vous aurez une quantité fixe d'utilisation de la mémoire même si vous n'avez pas besoin de toute cette mémoire tout le temps.

L'allocation de mémoire n'est-elle pas par définition un concept d'exécution?

La mémoire n'est pas utilisée avant l'exécution, mais juste avant l'exécution, son allocation est gérée par le système.

Si je crée une variable allouée statiquement de 1 Ko dans mon code C / C ++, cela augmentera-t-il la taille de l'exécutable du même montant?

Le simple fait de déclarer la valeur statique n'augmentera pas la taille de votre exécutable de plus de quelques octets. Le déclarer avec une valeur initiale différente de zéro sera (pour conserver cette valeur initiale). Au contraire, l'éditeur de liens ajoute simplement cette quantité de 1 Ko à l'exigence de mémoire que le chargeur du système crée pour vous immédiatement avant l'exécution.

mah
la source
1
si j'écris static int i[4] = {2 , 3 , 5 ,5 }, il augmentera de la taille de l'exécutable de 16 octets. Vous avez dit "Le simple fait de déclarer la valeur statique n'augmentera pas la taille de votre exécutable de plus de quelques octets. Le déclarer avec une valeur initiale non nulle" Le déclarer avec une valeur initiale sera ce que cela signifie.
Suraj Jain
Votre exécutable a deux zones pour les données statiques - une pour la statique non initialisée et une pour la statique initialisée. La zone non initialisée n'est en réalité qu'une indication de taille; lorsque votre programme est exécuté, cette taille est utilisée pour agrandir la zone de stockage statique, mais le programme lui-même n'a pas eu à contenir plus que la quantité de données non initialisées utilisées. Pour les statiques initialisées, votre programme doit contenir non seulement la taille de (chaque) statique, mais également ce sur quoi il est initialisé. Ainsi, dans votre exemple, votre programme contiendra 2, 3, 5 et 5.
mah
C'est la mise en œuvre définie comme où il est placé / comment il est alloué, mais je ne suis pas sûr de comprendre le besoin de savoir.
mah
23

La mémoire allouée au moment de la compilation signifie que lorsque vous chargez le programme, une partie de la mémoire sera immédiatement allouée et la taille et la position (relative) de cette allocation sont déterminées au moment de la compilation.

char a[32];
char b;
char c;

Ces 3 variables sont "allouées au moment de la compilation", cela signifie que le compilateur calcule leur taille (qui est fixe) au moment de la compilation. La variable asera un offset en mémoire, disons, pointant vers l'adresse 0, bpointera vers l'adresse 33 et cvers 34 (en supposant aucune optimisation d'alignement). Ainsi, allouer 1 Ko de données statiques n'augmentera pas la taille de votre code , car cela changera simplement un décalage à l'intérieur. L'espace réel sera alloué au moment du chargement .

L'allocation de mémoire réelle se produit toujours au moment de l'exécution, car le noyau doit en garder une trace et mettre à jour ses structures de données internes (la quantité de mémoire allouée à chaque processus, pages, etc.). La différence est que le compilateur connaît déjà la taille de chaque donnée que vous allez utiliser et celle-ci est allouée dès que votre programme est exécuté.

Souvenez-vous également que nous parlons d' adresses relatives . L'adresse réelle où se trouvera la variable sera différente. Au moment du chargement, le noyau réservera de la mémoire pour le processus, disons à l'adresse x, et toutes les adresses codées en dur contenues dans le fichier exécutable seront incrémentées d' xoctets, de sorte que la variable adans l'exemple sera à l'adresse x, b à l'adresse x+33et bientôt.

fede1024
la source
17

L'ajout de variables sur la pile qui occupent N octets n'augmente pas (nécessairement) la taille du bac de N octets. En fait, il n'ajoutera que quelques octets la plupart du temps.
Commençons par un exemple de la façon dont l' ajout d' un 1000 caractères à votre code va augmenter la taille du bac de manière linéaire.

Si le 1k est une chaîne, de mille caractères, qui est déclarée comme ceci

const char *c_string = "Here goes a thousand chars...999";//implicit \0 at end

et vous deviez alors vim your_compiled_bin, vous pourriez réellement voir cette chaîne dans la corbeille quelque part. Dans ce cas, oui: l'exécutable sera 1 k plus grand, car il contient la chaîne en entier.
Si, cependant, vous allouez un tableau de ints, chars ou longs sur la pile et l'assignez dans une boucle, quelque chose du genre

int big_arr[1000];
for (int i=0;i<1000;++i) big_arr[i] = some_computation_func(i);

alors non: cela n'augmentera pas le bac ... par 1000*sizeof(int)
Allocation au moment de la compilation signifie ce que vous avez maintenant compris que cela signifie (basé sur vos commentaires): le bac compilé contient des informations dont le système a besoin pour savoir combien de mémoire de quelle fonction / bloc aura besoin lors de son exécution, ainsi que des informations sur la taille de pile dont votre application a besoin. C'est ce que le système allouera lorsqu'il exécutera votre bac, et votre programme deviendra un processus (enfin, l'exécution de votre bac est le processus qui ... eh bien, vous comprenez ce que je dis).
Bien sûr, je ne brosse pas l'image complète ici: le bac contient des informations sur la taille d'une pile dont le bac aura réellement besoin. Sur la base de ces informations (entre autres), le système réservera une partie de la mémoire, appelée pile, sur laquelle le programme obtiendra une sorte de règne libre. La mémoire de pile est toujours allouée par le système lorsque le processus (le résultat de l'exécution de votre bac) est lancé. Le processus gère ensuite la mémoire de la pile pour vous. Lorsqu'une fonction ou une boucle (tout type de bloc) est invoquée / est exécutée, les variables locales à ce bloc sont poussées vers la pile, et elles sont supprimées (la mémoire de la pile est "libérée" pour ainsi dire) pour être utilisées par d'autres fonctions / blocs. Alors déclarantint some_array[100]ajoutera seulement quelques octets d'informations supplémentaires à la corbeille, ce qui indique au système que la fonction X nécessitera 100*sizeof(int)+ un espace de comptabilité supplémentaire.

Elias Van Ootegem
la source
Merci beaucoup. Une autre question, est-ce que les variables locales pour les fonctions sont également allouées de la même manière pendant la compilation?
Talha Sayed le
@TalhaSayed: Oui, c'est ce que je voulais dire quand j'ai dit: "informations dont le système a besoin pour savoir combien de mémoire quelle fonction / bloc nécessitera." Au moment où vous appelez une fonction, le système allouera la mémoire requise pour cette fonction. Au moment où la fonction revient, cette mémoire sera à nouveau libérée.
Elias Van Ootegem
Quant aux commentaires dans votre code C: Ce n'est pas réellement / nécessairement ce qui se passe. Par exemple, la chaîne ne sera probablement allouée qu'une seule fois, au moment de la compilation. Ainsi, il n'est jamais "libéré" (je pense aussi que la terminologie n'est généralement utilisée que lorsque vous allouez quelque chose de manière dynamique), in'est pas "libéré" ou non plus. S'il idevait résider dans la mémoire, il serait simplement poussé dans la pile, quelque chose qui n'est pas libéré dans ce sens du mot, sans tenir compte de cela iou cqui sera conservé dans les registres tout le temps. Bien sûr, tout dépend du compilateur, ce qui signifie que ce n'est pas si noir et blanc.
phant0m
@ phant0m: Je n'ai jamais dit que la chaîne était allouée sur la pile, seulement le pointeur aussi, la chaîne elle-même résiderait dans la mémoire en lecture seule. Je sais que la mémoire associée aux variables locales ne se libère pas dans le sens des free()appels, mais la mémoire de pile qu'ils ont utilisée est libre pour une utilisation par d'autres fonctions une fois que la fonction que j'ai énumérée est retournée. J'ai supprimé le code, car il peut être déroutant pour certains
Elias Van Ootegem
Ah, je vois. Dans ce cas, prenez mon commentaire comme signifiant "J'ai été confus par votre formulation".
phant0m
16

Sur de nombreuses plates-formes, toutes les allocations globales ou statiques de chaque module seront consolidées par le compilateur en trois allocations consolidées ou moins (une pour les données non initialisées (souvent appelées "bss"), une pour les données inscriptibles initialisées (souvent appelées "données") ), et une pour les données constantes ("const")), et toutes les allocations globales ou statiques de chaque type dans un programme seront consolidées par l'éditeur de liens en un global pour chaque type. Par exemple, en supposant qu'il ints'agit de quatre octets, un module a les éléments suivants comme seules allocations statiques:

int a;
const int b[6] = {1,2,3,4,5,6};
char c[200];
const int d = 23;
int e[4] = {1,2,3,4};
int f;

il indiquerait à l'éditeur de liens qu'il avait besoin de 208 octets pour bss, 16 octets pour "data" et 28 octets pour "const". De plus, toute référence à une variable serait remplacée par un sélecteur de zone et un décalage, donc a, b, c, d et e, seraient remplacés par bss + 0, const + 0, bss + 4, const + 24, data +0, ou bss + 204, respectivement.

Lorsqu'un programme est lié, toutes les zones bss de tous les modules sont concaténées ensemble; de même les zones de données et const. Pour chaque module, l'adresse de toutes les variables relatives au bss sera augmentée de la taille des zones bss de tous les modules précédents (encore une fois, de même avec data et const). Ainsi, lorsque l'éditeur de liens est terminé, tout programme aura une allocation bss, une allocation de données et une allocation const.

Lorsqu'un programme est chargé, l'une des quatre choses se produira généralement en fonction de la plate-forme:

  1. L'exécutable indiquera le nombre d'octets dont il a besoin pour chaque type de données et - pour la zone de données initialisée, où se trouve le contenu initial. Il comprendra également une liste de toutes les instructions qui utilisent une adresse relative bss, data ou const. Le système d'exploitation ou le chargeur allouera la quantité d'espace appropriée pour chaque zone, puis ajoutera l'adresse de départ de cette zone à chaque instruction qui en a besoin.

  2. Le système d'exploitation allouera un morceau de mémoire pour contenir les trois types de données et donnera à l'application un pointeur vers ce bloc de mémoire. Tout code qui utilise des données statiques ou globales le déréférencera par rapport à ce pointeur (dans de nombreux cas, le pointeur sera stocké dans un registre pour la durée de vie d'une application).

  3. Le système d'exploitation n'allouera initialement aucune mémoire à l'application, sauf pour ce qui contient son code binaire, mais la première chose que fera l'application sera de demander une allocation appropriée au système d'exploitation, qu'elle conservera pour toujours dans un registre.

  4. Le système d'exploitation n'allouera initialement pas d'espace pour l'application, mais l'application demandera une allocation appropriée au démarrage (comme ci-dessus). L'application inclura une liste d'instructions avec des adresses qui doivent être mises à jour pour refléter où la mémoire a été allouée (comme avec le premier style), mais plutôt que d'avoir l'application corrigée par le chargeur du système d'exploitation, l'application inclura suffisamment de code pour se patcher. .

Les quatre approches présentent des avantages et des inconvénients. Dans tous les cas, cependant, le compilateur consolidera un nombre arbitraire de variables statiques en un petit nombre fixe de demandes de mémoire, et l'éditeur de liens consolidera toutes celles-ci en un petit nombre d'allocations consolidées. Même si une application devra recevoir un morceau de mémoire du système d'exploitation ou du chargeur, ce sont le compilateur et l'éditeur de liens qui sont responsables d'allouer des éléments individuels de ce gros morceau à toutes les variables individuelles qui en ont besoin.

supercat
la source
13

Le cœur de votre question est la suivante: "Comment la mémoire est-elle" allouée "dans un fichier compilé? La mémoire n'est-elle pas toujours allouée dans la RAM avec tout le matériel de gestion de la mémoire virtuelle? L'allocation de mémoire n'est-elle pas par définition un concept d'exécution?"

Je pense que le problème est qu'il y a deux concepts différents impliqués dans l'allocation de mémoire. À sa base, l'allocation de mémoire est le processus par lequel nous disons "cet élément de données est stocké dans ce bloc de mémoire spécifique". Dans un système informatique moderne, cela implique un processus en deux étapes:

  • Certains systèmes sont utilisés pour décider de l'adresse virtuelle à laquelle l'élément sera stocké
  • L'adresse virtuelle est mappée à une adresse physique

Le dernier processus est purement d'exécution, mais le premier peut être effectué au moment de la compilation, si les données ont une taille connue et qu'un nombre fixe d'entre elles est requis. Voici comment cela fonctionne:

  • Le compilateur voit un fichier source contenant une ligne qui ressemble un peu à ceci:

    int c;
  • Il produit une sortie pour l'assembleur qui lui demande de réserver de la mémoire pour la variable «c». Cela pourrait ressembler à ceci:

    global _c
    section .bss
    _c: resb 4
  • Lorsque l'assembleur s'exécute, il conserve un compteur qui suit les décalages de chaque élément à partir du début d'un «segment» de mémoire (ou «section»). C'est comme les parties d'un très grand 'struct' qui contient tout dans le fichier entier, il n'a pas de mémoire réelle allouée à ce moment, et pourrait être n'importe où. Il note dans une table qui _ca un décalage particulier (disons 510 octets à partir du début du segment) puis incrémente son compteur de 4, donc la prochaine variable sera à (par exemple) 514 octets. Pour tout code nécessitant l'adresse de _c, il met simplement 510 dans le fichier de sortie et ajoute une note indiquant que la sortie a besoin de l'adresse du segment qui contient l' _cajout ultérieur.

  • L'éditeur de liens prend tous les fichiers de sortie de l'assembleur et les examine. Il détermine une adresse pour chaque segment afin qu'ils ne se chevauchent pas et ajoute les décalages nécessaires pour que les instructions se réfèrent toujours aux éléments de données corrects. Dans le cas d'une mémoire non initialisée comme celle occupée parc(on a dit à l'assembleur que la mémoire ne serait pas initialisée par le fait que le compilateur la place dans le segment '.bss', qui est un nom réservé à la mémoire non initialisée), il inclut un champ d'en-tête dans sa sortie qui indique au système d'exploitation combien doit être réservé. Il peut être déplacé (et l'est généralement), mais il est généralement conçu pour être chargé plus efficacement à une adresse mémoire particulière, et le système d'exploitation essaiera de le charger à cette adresse. À ce stade, nous avons une assez bonne idée de l'adresse virtuelle qui sera utilisée c.

  • L'adresse physique ne sera pas réellement déterminée tant que le programme ne sera pas en cours d'exécution. Cependant, du point de vue du programmeur, l'adresse physique est en fait sans importance - nous ne saurons même jamais ce que c'est, car le système d'exploitation ne prend généralement pas la peine de le dire à personne, il peut changer fréquemment (même lorsque le programme est en cours d'exécution), et un L'objectif principal du système d'exploitation est de supprimer cela de toute façon.

Jules
la source
9

Un exécutable décrit l'espace à allouer pour les variables statiques. Cette allocation est effectuée par le système, lorsque vous exécutez l'exécutable. Ainsi, votre variable statique de 1 Ko n'augmentera pas la taille de l'exécutable de 1 Ko:

static char[1024];

Sauf bien sûr que vous spécifiez un initialiseur:

static char[1024] = { 1, 2, 3, 4, ... };

Ainsi, en plus du «langage machine» (c'est-à-dire des instructions CPU), un exécutable contient une description de la configuration de la mémoire requise.

le sens compte
la source
5

La mémoire peut être allouée de plusieurs manières:

  • dans le tas d'application (le tas entier est alloué pour votre application par le système d'exploitation lorsque le programme démarre)
  • dans le tas du système d'exploitation (vous pouvez donc en saisir de plus en plus)
  • dans le tas contrôlé par le garbage collector (comme les deux ci-dessus)
  • sur la pile (afin que vous puissiez obtenir un débordement de pile)
  • réservé dans le segment de code / données de votre binaire (exécutable)
  • dans un endroit distant (fichier, réseau - et vous recevez un handle pas un pointeur vers cette mémoire)

Maintenant votre question est de savoir quelle est la "mémoire allouée au moment de la compilation". Il s'agit certainement d'un dicton mal formulé, censé faire référence à l'allocation de segment binaire ou à l'allocation de pile, ou dans certains cas même à une allocation de tas, mais dans ce cas, l'allocation est cachée aux yeux du programmeur par un appel de constructeur invisible. Ou probablement la personne qui a dit cela voulait juste dire que la mémoire n'est pas allouée sur le tas, mais qu'elle ne connaissait pas les allocations de pile ou de segment (ou ne voulait pas entrer dans ce genre de détails).

Mais dans la plupart des cas, la personne veut simplement dire que la quantité de mémoire allouée est connue au moment de la compilation .

La taille binaire ne changera que lorsque la mémoire est réservée dans le code ou le segment de données de votre application.

exebook
la source
1
Cette réponse est déroutante (ou confuse) dans la mesure où elle parle de "le tas d'application", "le tas du système d'exploitation" et "le tas GC" comme s'il s'agissait de concepts significatifs. J'en déduis qu'en n ° 1 vous essayiez de dire que certains langages de programmation pourraient (hypothétiquement) utiliser un schéma "d'allocation de tas" qui alloue de la mémoire à partir d'un tampon de taille fixe dans la section .data, mais cela semble suffisamment irréaliste pour être nuisible à la compréhension du PO. Concernant les # 2 et # 3, la présence d'un GC ne change vraiment rien. Et re # 5, vous avez omis la distinction relativement BEAUCOUP plus importante entre .dataet .bss.
Quuxplusone
4

Vous avez raison. La mémoire est effectivement allouée (paginée) au moment du chargement, c'est-à-dire lorsque le fichier exécutable est introduit dans la mémoire (virtuelle). La mémoire peut également être initialisée à ce moment. Le compilateur crée simplement une carte mémoire. [À propos, les espaces de pile et de tas sont également alloués au moment du chargement!]

Yves Daoust
la source
2

Je pense que vous devez prendre un peu de recul. Mémoire allouée au moment de la compilation ... Qu'est-ce que cela signifie? Cela peut-il signifier que la mémoire sur des puces qui n'ont pas encore été fabriquées, pour des ordinateurs qui n'ont pas encore été conçus, est en quelque sorte réservée? Non, voyage dans le temps, pas de compilateurs capables de manipuler l'univers.

Donc, cela doit signifier que le compilateur génère des instructions pour allouer cette mémoire d'une manière ou d'une autre à l'exécution. Mais si vous le regardez sous le bon angle, le compilateur génère toutes les instructions, alors quelle peut être la différence. La différence est que le compilateur décide, et au moment de l'exécution, votre code ne peut pas changer ou modifier ses décisions. S'il a décidé qu'il avait besoin de 50 octets au moment de la compilation, au moment de l'exécution, vous ne pouvez pas le décider d'allouer 60 octets - cette décision a déjà été prise.

jmoreno
la source
J'aime les réponses qui utilisent la méthode socratique, mais je vous ai quand même défavorisée pour la conclusion erronée que "le compilateur génère des instructions pour allouer cette mémoire d'une manière ou d'une autre à l'exécution". Consultez la réponse la plus populaire pour voir comment un compilateur peut "allouer de la mémoire" sans générer aucune "instruction" d'exécution. (Notez que «instructions» dans un contexte de langage d'assemblage a une signification spécifique, c'est-à-dire, des opcodes exécutables. Vous pourriez avoir utilisé le mot familièrement pour signifier quelque chose comme «recette», mais dans ce contexte, cela ne fera que confondre l'OP. )
Quuxplusone
1
@Quuxplusone: J'ai lu (et j'ai voté pour) cette réponse. Et non, ma réponse n'aborde pas spécifiquement le problème des variables initialisées. Il ne traite pas non plus du code auto-modifiable. Bien que cette réponse soit excellente, elle n'a pas abordé ce que je considère comme une question importante - mettre les choses en contexte. D'où ma réponse, qui, je l'espère, aidera le PO (et d'autres) à s'arrêter et à réfléchir à ce qui se passe ou peut se passer, lorsqu'ils ont des problèmes qu'ils ne comprennent pas.
jmoreno
@Quuxplusone: Désolé si je fais de fausses allégations ici, mais je suppose que vous étiez aussi l'une des personnes qui ont répondu à ma réponse. Si tel est le cas, cela vous dérangerait-il de préciser quelle partie de ma réponse était la principale raison de le faire, et voudriez-vous également vérifier ma modification? Je sais que j'ai sauté quelques bits sur les véritables éléments internes de la gestion de la mémoire de la pile, alors j'ai maintenant ajouté un peu sur le fait que je n'étais pas précis à 100% à ma réponse maintenant de toute façon :)
Elias Van Ootegem
@jmoreno Le point que vous avez dit à propos de "Cela peut-il signifier que la mémoire sur des puces qui n'ont pas encore été fabriquées, pour des ordinateurs qui n'ont pas encore été conçus, est en quelque sorte réservée? Non." C'est exactement le faux sens qu'implique le mot «allocation» qui m'a dérouté dès le départ. J'aime cette réponse parce qu'elle fait référence exactement au problème que j'essayais de signaler. Aucune des réponses ici n'a vraiment touché ce point particulier. Merci.
Talha Sayed
2

Si vous apprenez la programmation d'assemblage, vous verrez que vous devez tailler des segments pour les données, la pile et le code, etc. Le segment de données est l'endroit où vivent vos chaînes et vos nombres. Le segment de code est l'endroit où vit votre code. Ces segments sont intégrés au programme exécutable. Bien sûr, la taille de la pile est également importante ... vous ne voudriez pas d'un débordement de pile !

Donc, si votre segment de données est de 500 octets, votre programme a une zone de 500 octets. Si vous modifiez le segment de données à 1500 octets, la taille du programme sera de 1000 octets plus grande. Les données sont assemblées dans le programme réel.

C'est ce qui se passe lorsque vous compilez des langages de niveau supérieur. La zone de données réelle est allouée lorsqu'elle est compilée dans un programme exécutable, ce qui augmente la taille du programme. Le programme peut également demander de la mémoire à la volée, et il s'agit de mémoire dynamique. Vous pouvez demander de la mémoire à la RAM et le CPU vous la donnera à utiliser, vous pouvez la laisser aller, et votre garbage collector la restituera au CPU. Il peut même être échangé sur un disque dur, si nécessaire, par un bon gestionnaire de mémoire. Ces fonctionnalités sont ce que vous offrent les langages de haut niveau.

Ingénieur
la source
2

Je voudrais expliquer ces concepts à l'aide de quelques schémas.

C'est vrai que la mémoire ne peut pas être allouée au moment de la compilation, bien sûr. Mais, alors ce qui se passe en fait au moment de la compilation.

Voici l'explication. Disons, par exemple, qu'un programme a quatre variables x, y, z et k. Maintenant, au moment de la compilation, il fait simplement une carte mémoire, où l'emplacement de ces variables les unes par rapport aux autres est déterminé. Ce diagramme l'illustrera mieux.

Imaginez maintenant qu'aucun programme ne fonctionne en mémoire. Je le montre par un grand rectangle vide.

champ vide

Ensuite, la première instance de ce programme est exécutée. Vous pouvez le visualiser comme suit. C'est l'heure à laquelle la mémoire est allouée.

première instance

Lorsque la deuxième instance de ce programme est en cours d'exécution, la mémoire ressemblerait à ceci.

deuxième instance

Et le troisième ...

troisième instance

Etc., etc.

J'espère que cette visualisation explique bien ce concept.

user3258051
la source
2
Si ces diagrammes montraient la différence entre la mémoire statique et dynamique, ils seraient plus utiles à mon humble avis.
Bartek Banachewicz
Cela avait été délibérément évité par moi pour garder les choses simples. Mon objectif est d'expliquer ce funda avec clarté sans trop de fouillis technique. Dans la mesure où cela est destiné à la variable statique .. Ce point a été bien établi par les réponses précédentes. J'ai donc sauté ceci.
user3258051
1
Eh, ce concept n'est pas particulièrement compliqué, donc je ne vois pas pourquoi le rendre plus simple qu'il ne devrait l'être, mais comme il ne s'agit que d'une réponse complémentaire, d'accord.
Bartek Banachewicz