62 votes

Pourquoi les adresses mémoire des littéraux de chaîne sont-elles si différentes de celles des autres, sous Linux?

J'ai remarqué que les littéraux de chaîne sont très différentes des adresses dans la mémoire que les autres variables et des constantes (OS Linux): ils ont beaucoup de zéros (pas imprimé).

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 l' .rodata de la partie de l'exécutable. Est-il une façon spéciale de l'OS gère par la suite, de sorte que les littéraux de finir dans une zone spéciale de la mémoire (avec des zéros à gauche)? Quels sont les avantages de cet emplacement de la mémoire ou est-il quelque chose de spécial à ce sujet?

73voto

ThorX89 Points 967

Voici comment le processus de la mémoire est posé sur Linux (à partir de http://www.thegeekstuff.com/2012/03/linux-processes-memory-layout/):

Linux process memory layout

L' .rodata l'article est protégé en écriture paragraphe de la Initialisé Données Globalesbloc. (Un article de ELF exécutables désigner .de données est son écriture de contrepartie pour l'écriture variables globales initialisées à des valeurs non nulles. Inscriptible variables globales initialisées à zéros aller à l' .sev bloc. Par globals ici, je veux dire les variables globales et toutes statique des variables indépendamment de placement.)

L'image doit expliquer les valeurs numériques de vos adresses.

Si vous voulez approfondir la question, puis sur Linux, vous pouvez consulter le /proc/$pid/maps fichiers virtuels qui décrivent la disposition de la mémoire du processus en cours d'exécution. Vous n'aurez pas réservé (commençant par un point) ELFE de la section de noms, mais vous pouvez deviner que l'ELFE de la section d'un bloc de mémoire est originaire de en regardant sa protection de la mémoire des drapeaux. Par exemple, l'exécution de

$ 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 bloc certainement venu de .texte (code exécutable), la première r--p - bloc de .rodata, et le suivant rw-- pâtés de maisons .sev et .les données. (Entre le tas et la pile de bloc sont des blocs chargés dynamiquement des bibliothèques liées par l'éditeur de liens dynamique.)


Remarque: pour se conformer À la norme, il faut jeter la int* pour "%p" de (void*) , sinon le comportement est indéfini.

15voto

Armen Tsirunyan Points 59548

En effet, les littéraux de chaîne ont une durée de stockage statique . C'est-à-dire qu'ils vivront pendant toute la durée du programme. De telles variables peuvent être stockées dans un emplacement de mémoire spécial qui ne se trouve ni sur le tas, ni sur la pile. D'où la différence d'adresses.

7voto

Steve Summit Points 16971

N'oubliez pas que l'emplacement d'un pointeur est différent de celui sur lequel il pointe . Une comparaison plus réaliste (de 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 désignent une région similaire de la mémoire.

1voto

Leslie Satenstein Points 154

Considérer que les littéraux sont variables en lecture seule et ainsi, il y a un concept d'un littéral de la piscine. Ce que le sens littéral de la piscine est une collection du programme unique de littéraux, où dupliquer les constantes sont jetés comme des références sont fusionnés en un seul.

Il est l'une littérale de la piscine pour chaque source, et selon le degré de sophistication du lien / bind programme, littéral piscines peuvent être placés les uns à côté des autres pour en créer un .rodata.

Il est également aucune garantie que le sens littéral de la piscine est en lecture seule protégé. Langue si compilateur conceptions de la traiter ainsi.

Envisager de mon fragment de code. Je pourrais avoir

const char *pc="bonjour le monde";
const char *cp1="bonjour le monde";

Le bon compilateur reconnaître que, dans le code source, la lecture seule des littéraux de cp, cp1,sont pointant vers des chaînes identiques, et fera point cp1 cp littérale, en rejetant la deuxième.

Un point de plus. Le sens littéral de la piscine peut être un multiple de 256bytes ou de valeur différente. Si le pool de données est inférieure à 256 octets, le relais sera complétée par des hex de zéros.

Différents compilateurs, suivre les standards de développement, permettant à un module compilé avec C, d'être en relation avec un module compilé avec l'assemblée de la langue ou une autre langue. Les deux littérale piscines sont placés de manière consécutive .rodata.

0voto

user2760751 Points 191
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.

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