181 votes

Que signifie "Mémoire allouée à la compilation"?

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

Compilation, ce que je comprends, convertit de haut niveau, C/C++ code en langage machine et génère un fichier exécutable. Comment la mémoire est "affecté dans un fichier compilé ? N'est-ce pas la mémoire toujours alloué dans la mémoire vive avec l'ensemble de la gestion de mémoire virtuelle de choses ?

N'est-ce pas l'allocation de la mémoire, par définition, un moteur d'exécution de concept ?

Si je fais une 1 KO allouée statiquement variable dans mon code C/C++, est-ce que l'augmentation de la taille de l'exécutable par le même montant ?

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

Retour À l'essentiel: l'allocation de Mémoire, une promenade dans l'histoire

202voto

Manu343726 Points 8803

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

Par exemple, considérons un réseau mondial:

int array[100];

Le compilateur sait au moment de la compilation, la taille du tableau et la taille de l' int, de sorte qu'il sait de la taille complète de la matrice au moment de la compilation. Aussi une variable globale est statique de la durée de stockage par défaut: il est alloué dans la mémoire statique de la zone de la mémoire du processus de l'espace (.de données/.bss). Compte tenu de cette information, le compilateur décide lors de la compilation de ce que l'adresse de cette zone de mémoire statique, le tableau sera.

Bien sûr que la mémoire les adresses sont des adresses virtuelles. Le programme suppose qu'elle a son propre ensemble de la mémoire de l'espace (à Partir de 0x00000000 à 0xFFFFFFFF par exemple). C'est pourquoi le compilateur pourrait faire des hypothèses comme "d'Accord, le tableau sera à l'adresse 0x00A33211". Au moment de l'exécution que les adresses sont convertis à la vraie/adresses matérielles par la MMU et les OS.

La valeur initialisée stockage statique, les choses sont un peu différentes. Par exemple:

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

Dans notre premier exemple, le compilateur ne décide de l'endroit où le tableau sera alloué, stocker cette information dans le fichier exécutable.
Dans le cas d'une valeur initialisée choses, le compilateur permet aussi d'injecter de la valeur initiale de la matrice dans l'exécutable, et ajoute le code qui indique le programme loader après le tableau de répartition au démarrage du programme, le tableau doit être rempli avec ces valeurs.

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

Le code C++:

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

int main()
{}

La sortie de l'assemblée:

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 injectés directement dans l'assemblée. Dans le tableau a, le compilateur génère un zéro initialisation de 16 octets, parce que la Norme dit que statique stockées choses doit être initialisé à zéro par défaut:

8.5.9 (Initialiseurs) [Note]:
Chaque objet de la statique de la durée de stockage est initialisé à zéro à le démarrage du programme, avant toute autre initialisation a lieu. Dans certains cas échéant, d'autres l'initialisation est faite plus tard.

J'ai toujours penser que les personnes à démontage leur code pour voir ce que le compilateur n'a vraiment avec le code C++. Cela s'applique à partir de classes de stockage/durée (comme cette question) à avancé les optimisations du compilateur. Vous pourriez demander à votre compilateur pour générer de l'assemblée, mais il y a de merveilleux outils pour ce faire sur l'Internet d'une manière amicale. Ma préférée est la GCC Explorer.

28voto

mah Points 21457

La mémoire allouée au moment de la compilation signifie simplement il n'y aura pas d'autre allocation au moment de l'exécution -- pas d'appels à malloc, de nouvelles, ou d'autres dynamiques des méthodes de répartition. Vous aurez une quantité fixe de l'utilisation de la mémoire même si vous n'avez pas besoin de tous que la mémoire de tous les temps.

N'est-ce pas l'allocation de la mémoire, par définition, un moteur d'exécution de concept ?

La mémoire n'est pas en usage avant le moment de l'exécution, mais immédiatement avant l'exécution de départ de sa répartition est gérée par le système.

Si je fais une 1 KO allouée statiquement variable dans mon code C/C++, est-ce que l'augmentation de la taille de l'exécutable par le même montant ?

Le simple fait de déclarer la statique ne sera pas augmenter la taille de votre exécutable plus que quelques octets. Déclarant avec une valeur initiale qui est non-zéro (dans l'ordre de tenir la valeur initiale). Plutôt, l'éditeur de liens ajoute simplement ce 1KB montant de l'exigence de mémoire que le système de chargeur crée pour vous immédiatement avant l'exécution.

23voto

fede1024 Points 1966

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

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

Ces 3 variables sont "attribués au moment de la compilation", cela signifie que le compilateur calcule leur taille (qui est fixe) au moment de la compilation. La variable a sera un décalage dans la mémoire, disons, pointant vers l'adresse 0, b se pointer à l'adresse 33 c à 34 (en supposant qu'aucun alignement d'optimisation). Ainsi, l'allocation de 1 ko de données statiques ne sera pas augmenter la taille de votre code, car il suffit de changer un décalage à l'intérieur. L'espace réel sera attribué au moment du chargement.

Réel de l'allocation de mémoire se produit toujours au moment de l'exécution, parce que le noyau a besoin de garder une trace de celui-ci et de mettre à jour ses structures de données internes (la quantité de mémoire allouée pour chaque processus, les pages et ainsi de suite). La différence est que le compilateur connaît déjà la taille de chacune des données que vous allez utiliser et c'est allouée dès que votre programme est exécuté.

Rappelez-vous aussi que nous parlons par des adresses relatives. L'adresse réelle où la variable sera situé sera différent. Au moment du chargement du noyau de la réserve de la mémoire pour le processus, permet de dire à l'adresse x, et tous les codée en dur les adresses contenues dans le fichier exécutable sera incrémenté par x octets, de sorte que la variable a dans l'exemple sera à l'adresse x, b à l'adresse x+33 et ainsi de suite.

17voto

Elias Van Ootegem Points 29404

L'ajout de variables sur la pile de prendre les N octets n'est pas (nécessairement) d'augmenter le bin de la taille de N octets. Il sera, en effet, ajouter un peu d'octets, la plupart du temps.
Commençons avec un exemple de la façon dont l'ajout d'un 1000 caractères de votre code va augmenter le bin de la taille d'une manière linéaire.

Si le 1k est une chaîne, d'un millier de chars, qui est déclarée comme

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

et vous, alors, étaient à l' vim your_compiled_bin, vous pensez réellement être en mesure de voir la chaîne dans la poubelle quelque part. Dans ce cas, oui: l'exécutable sera 1 k plus grand, parce qu'il contient la chaîne de caractères en entier.
Si, toutefois, vous allouer un tableau de ints, chars ou longs sur la pile et de l'affecter dans une boucle, quelque chose le long de ces lignes

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

alors, non: il ne sera pas augmenter la poubelle... en 1000*sizeof(int)
L'Allocation au moment de la compilation signifie que vous avez maintenant de venir à comprendre qu'il signifie (en fonction de vos commentaires): la compilation bin contient de l'information requise par le système pour connaître la quantité de mémoire que la fonction/bloc aura besoin quand il est exécuté, ainsi que des informations sur la taille de la pile de votre application. C'est ce que le système va allouer lorsqu'il exécute votre bac et votre programme devient un processus (ainsi, l'exécution de votre bac est le processus que... eh bien, vous obtenez ce que je dis).
Bien sûr, je ne suis pas la peinture de l'image complète ici: Les bin contient des informations sur comment grand une pile à la poubelle va effectivement besoin. Basé sur cette information (entre autres choses), le système de la réserve une partie de la mémoire, qu'on appelle la pile, que le programme obtient sorte de régner sur. Pile de mémoire allouée par le système, lorsque le processus (le résultat de votre bac de l'exécution est lancée. Le processus gère ensuite la pile de la mémoire pour vous. Lorsqu'une fonction ou d'une boucle (tout type de bloc) est invoquée/est exécuté, les variables locales à ce bloc sont poussés sur la pile, et ils sont supprimés (la pile de la mémoire est "libéré" façon de parler) pour être utilisée par d'autres fonctions/blocs. Donc, déclarant int some_array[100] ne fera qu'ajouter un peu d'octets d'informations supplémentaires pour la poubelle, qui indique au système que la fonction X a besoin d' 100*sizeof(int) + un livre de maintien de l'espace supplémentaire.

16voto

supercat Points 25534

Sur de nombreuses plates-formes, tous les globales ou statiques allocations au sein de chaque module sera consolidé par le compilateur en moins de trois consolidé dotations (un pour les données non initialisées (souvent appelé "bss"), un pour les initialisé écriture de données (souvent appelé "données"), et la constante de données ("const")), et toutes les globales ou statiques attributions de chaque type dans un programme sera consolidé par l'éditeur de liens dans un global pour chaque type. Par exemple, en supposant int est de quatre octets, un module est le suivant comme son seul statique affectations:

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

ce serait dire que l'éditeur de liens qu'il a besoin de 208 octets pour sev, 16 octets pour les "données", et de 28 octets pour "const". De plus, toute référence à une variable serait remplacé par une zone du sélecteur et de décalage, de sorte que a, b, c, d, et e, serait remplacé par bss+0, const+0, bss+4, const+24, données+0, ou bss+204, respectivement.

Quand un programme est lié, toutes les bss zones de tous les modules sont concaténées ensemble; de même, les données et const domaines. Pour chaque module, l'adresse de toute bss-par rapport à des variables sera augmenté par la taille de tous les précédents modules bss zones (encore une fois, même avec des données et des const). Ainsi, lorsque l'éditeur de liens est exécuté, le programme aura un sev de répartition, une répartition de données, et un const allocation.

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

  1. L'exécutable indique combien d'octets sont nécessaires pour chaque type de données et ce, pour la initialisé zone de données, dont le contenu peut être trouvé. Il comprend également une liste de toutes les instructions qui utilisent un bss-, de données, ou const adresse relative. Le système d'exploitation ou le chargeur d'allouer le montant approprié de l'espace pour chaque zone, puis ajouter l'adresse de départ de la zone à chaque instruction qui en a besoin.

  2. Le système d'exploitation va allouer un bloc de mémoire pour contenir tous les trois types de données, et de donner à l'application d'un pointeur vers cette partie de la mémoire. Tout code qui utilise des données globales ou statiques seront déréférencement d'elle par rapport à celle du pointeur (dans de nombreux cas, le pointeur sera stockée dans un registre pour la durée de vie d'une application).

  3. Le système d'exploitation sera d'abord de ne pas allouer de la mémoire de l'application, sauf pour ce qui retient son code binaire, mais la première chose qu'il fait sera de demander une répartition à partir du système d'exploitation, qui sera pour toujours garder dans un registre.

  4. Le système d'exploitation sera d'abord de ne pas allouer de l'espace pour l'application, mais l'application va demander une répartition au démarrage (comme ci-dessus). La demande comprendra une liste d'instructions avec les adresses qui doivent être mis à 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és par le chargeur du système d'exploitation, la demande doit contenir le code nécessaire à l'application des correctifs.

Tous les quatre approches ont des avantages et des inconvénients. Dans tous les cas, cependant, le compilateur regroupe un nombre arbitraire de variables statiques dans un fixe petit nombre de demandes de mémoire, et l'éditeur de liens se regrouper tous dans un petit nombre d'états des allocations. Même si une application devront recevoir une partie de la mémoire du système d'exploitation ou du chargeur, c'est le compilateur et l'éditeur de liens qui sont responsables de l'allocation des pièces individuelles de ce gros morceau pour toutes les variables individuelles qui en ont besoin.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X