J'ai remarqué que les littéraux de chaîne ont des adresses en mémoire très différentes de celles des autres constantes et variables (Linux OS): ils ont de nombreux zéros non significatifs (non imprimés).
Exemple:
const char *h = "Hi";
int i = 1;
printf ("%p\n", (void *) h);
printf ("%p\n", (void *) &i);
Sortie:
0x400634
0x7fffc1ef1a4c
Je sais qu'ils sont stockés dans le .rodata
partie de l'exécutable. Y a-t-il une façon spéciale dont le système d'exploitation le gère ensuite, de sorte que les littéraux se retrouvent dans une zone de mémoire spéciale (avec des zéros en tête)? Cet emplacement de mémoire présente-t-il des avantages ou y a-t-il quelque chose de spécial?
Voici comment la mémoire de processus est disposée sur Linux (à partir de http://www.thegeekstuff.com/2012/03/linux-processes-memory-layout/ ):
La section . Rodata est une sous-section protégée en écriture du bloc Initialized Global Data. (Une section qui ELF les exécutables désignent . Data est son pendant accessible en écriture pour writable globaux initialisés à des valeurs différentes de zéro. Les globaux inscriptibles initialisés à des zéros vont dans le bloc . bss . Par globaux, je veux dire les variables globales et tout variables statiques quel que soit le placement.)
L'image doit expliquer les valeurs numériques de vos adresses.
Si vous souhaitez approfondir votre recherche, sous Linux, vous pouvez inspecter les fichiers virtuels /proc/$ pid/maps qui décrivent la configuration de la mémoire des processus en cours d'exécution. Vous n'obtiendrez pas les noms de section ELF réservés (commençant par un point), mais vous pouvez deviner de quelle section ELF un bloc de mémoire est issu en regardant ses indicateurs de protection de la mémoire. Par exemple, exécuter
$ cat /proc/self/maps #cat's memory map
donne moi
00400000-0040b000 r-xp 00000000 fc:00 395465 /bin/cat
0060a000-0060b000 r--p 0000a000 fc:00 395465 /bin/cat
0060b000-0060d000 rw-p 0000b000 fc:00 395465 /bin/cat
006e3000-00704000 rw-p 00000000 00:00 0 [heap]
3000000000-3000023000 r-xp 00000000 fc:00 3026487 /lib/x86_64-linux-gnu/ld-2.19.so
3000222000-3000223000 r--p 00022000 fc:00 3026487 /lib/x86_64-linux-gnu/ld-2.19.so
3000223000-3000224000 rw-p 00023000 fc:00 3026487 /lib/x86_64-linux-gnu/ld-2.19.so
3000224000-3000225000 rw-p 00000000 00:00 0
3000400000-30005ba000 r-xp 00000000 fc:00 3026488 /lib/x86_64-linux-gnu/libc-2.19.so
30005ba000-30007ba000 ---p 001ba000 fc:00 3026488 /lib/x86_64-linux-gnu/libc-2.19.so
30007ba000-30007be000 r--p 001ba000 fc:00 3026488 /lib/x86_64-linux-gnu/libc-2.19.so
30007be000-30007c0000 rw-p 001be000 fc:00 3026488 /lib/x86_64-linux-gnu/libc-2.19.so
30007c0000-30007c5000 rw-p 00000000 00:00 0
7f49eda93000-7f49edd79000 r--p 00000000 fc:00 2104890 /usr/lib/locale/locale-archive
7f49edd79000-7f49edd7c000 rw-p 00000000 00:00 0
7f49edda7000-7f49edda9000 rw-p 00000000 00:00 0
7ffdae393000-7ffdae3b5000 rw-p 00000000 00:00 0 [stack]
7ffdae3e6000-7ffdae3e8000 r--p 00000000 00:00 0 [vvar]
7ffdae3e8000-7ffdae3ea000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
La première r-xp
le bloc provenait définitivement de . text (code exécutable), le premier r--p
block from . rodata , and the following rw - blocks from .bss et . data . (Entre le tas et le bloc de pile se trouvent des blocs chargés à partir de bibliothèques liées dynamiquement par l'éditeur de liens dynamique.)
Remarque: Pour respecter la norme, vous devez lancer le int*
pour "%p"
à (void*)
ou bien le comportement n'est pas défini.
C'est parce que les littéraux de chaîne ont durée de stockage statique. Autrement dit, ils vivront pendant tout le programme. Ces variables peuvent être stockées dans un emplacement de mémoire spécial qui n'est ni sur le tas ni sur la pile. D'où la différence d'adresses.
Rappelez-vous que là où un pointeur est est différent de l'endroit où un pointeur pointe vers. Une comparaison plus réaliste (pommes à pommes) serait
printf ("%p\n", (void *) &h);
printf ("%p\n", (void *) &i);
Je suppose que vous constaterez que h
et p
ont des adresses similaires. Ou, une autre comparaison plus réaliste serait
static int si = 123;
int *ip = &si;
printf ("%p\n", (void *) h);
printf ("%p\n", (void *) ip);
Je suppose que vous constaterez que h
et ip
pointent vers une région de mémoire similaire.
Considérez que les littéraux sont des variables en lecture seule et qu'il existe également un concept de pool de littéraux. Le pool littéral est un ensemble de littéraux uniques du programme, où les constantes en double sont supprimées lorsque les références sont fusionnées en une seule.
Il existe un pool littéral pour chaque source et, selon la sophistication du programme link/bind, des pools littéraux peuvent être placés côte à côte pour créer un .rodata.
Il n'y a également aucune garantie que le pool littéral est protégé en lecture seule. Le langage, bien que les conceptions du compilateur le traitent comme tel.
Considérez mon fragment de code. J'aurais pu
const char * cp = "bonjour le monde";
const char * cp1 = "hello world";
Le bon compilateur reconnaîtra que dans ce code source, les littéraux en lecture seule cp, cp1, pointent vers des chaînes identiques, et feront pointer cp1 vers le littéral de cp, en rejetant le second.
Un dernier point. Le pool littéral peut être un multiple de 256 octets ou une valeur différente. Si les données du pool sont inférieures à 256 octets, la marge sera complétée par des zéros hexadécimaux.
Différents compilateurs, suivent des normes de développement communes, permettant à un module compilé avec C , d'être lié à un module compilé avec langage d'assemblage ou autre langue. Les deux pools littéraux sont placés consécutivement dans .rodata.
printf ("%p\n", h); // h is the address of "Hi", which is in the rodata or other segments of the application.
printf ("%p\n", &i); // I think "i" is not a global variable, so &i is in the stack of main. The stack address is by convention in the top area of the memory space of the process.