J'ai remarqué quelque chose d'étrange après avoir compilé ce code sur ma machine:
#include <stdio.h>
int main()
{
printf("Hello, World!\n");
int a,b,c,d;
int e,f,g;
long int h;
printf("The addresses are:\n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x",
&a,&b,&c,&d,&e,&f,&g,&h);
return 0;
}
Le résultat est le suivant. Notez qu'entre chaque adresse int, il y a une différence de 4 octets. Cependant, entre le dernier entier et le long entier, il y a une différence de 12 octets:
Hello, World!
The addresses are:
da54dcac
da54dca8
da54dca4
da54dca0
da54dc9c
da54dc98
da54dc94
da54dc88
int
aprèsh
dans le code source. Le compilateur peut le mettre dans l'espace, avanth
.sizeof
fonction pour ça.printf("size: %d ", sizeof(long));
%x
. Heureusement pour vous, il se trouve que cela fonctionne correctement sur votre plate-forme pour passer des arguments de pointeur avec une chaîne de format attendueunsigned int
, mais les pointeurs et les entrées sont de tailles différentes dans de nombreux ABI. Permet%p
d'imprimer des pointeurs en code portable. (Il est facile d'imaginer un système dans lequel votre code imprimerait les moitiés supérieure / inférieure des 4 premiers pointeurs, au lieu de la moitié inférieure des 8.)%zu
. @yoyo_fun pour imprimer les adresses utilisées%p
. Utiliser le mauvais spécificateur de format appelle un comportement indéfiniRéponses:
Cela n'a pas pris 12 octets, il n'en a fallu que 8. Cependant, l' alignement par défaut pour un entier de 8 octets sur cette plate-forme est de 8 octets. En tant que tel, le compilateur devait déplacer le long int vers une adresse qui est divisible par 8. L'adresse "évidente", da54dc8c, n'est pas divisible par 8 d'où l'écart de 12 octets.
Vous devriez pouvoir tester cela. Si vous ajoutez un autre int avant le long, donc il y en a 8, vous devriez trouver que le long int sera bien aligné sans bouger. Ce ne sera plus que 8 octets à partir de l'adresse précédente.
Il vaut probablement la peine de souligner que, bien que ce test devrait fonctionner, vous ne devez pas vous fier aux variables organisées de cette façon. Le compilateur AC est autorisé à faire toutes sortes de choses géniales pour essayer de faire fonctionner votre programme rapidement, y compris en réorganisant les variables (avec quelques mises en garde).
la source
En effet, votre compilateur génère un remplissage supplémentaire entre les variables pour garantir qu'elles sont correctement alignées en mémoire.
Sur la plupart des processeurs modernes, si une valeur a une adresse qui est un multiple de sa taille, il est plus efficace d'y accéder. S'il avait été placé
h
au premier emplacement disponible, son adresse aurait été 0xda54dc8c, qui n'est pas un multiple de 8, donc aurait été moins efficace à utiliser. Le compilateur le sait et ajoute un peu d'espace inutilisé entre vos deux dernières variables afin de s'assurer que cela se produit.la source
Votre test ne teste pas nécessairement ce que vous pensez qu'il est, car il n'y a aucune exigence de la langue pour relier l'adresse de l'une de ces variables locales les unes aux autres.
Vous devez les placer sous forme de champs dans une structure afin de pouvoir déduire quelque chose sur l'allocation de stockage.
Les variables locales ne sont pas tenues de partager le stockage côte à côte d'une manière particulière. Le compilateur peut insérer une variable temporaire n'importe où dans la pile, par exemple, qui pourrait se trouver entre deux de ces variables locales.
En revanche, il ne serait pas autorisé d'insérer une variable temporaire dans une structure, donc si vous imprimiez les adresses des champs de structure à la place, vous compareriez les éléments prévus alloués à partir du même mandrin logique de mémoire (la structure).
la source