为什么Linux上的string文字的内存地址与其他string的内存地址不同?

我注意到string文字在内存中的地址与其他常量和variables(Linux OS)非常不同:它们有许多前导零(未打印)。

例:

const char *h = "Hi"; int i = 1; printf ("%p\n", (void *) h); printf ("%p\n", (void *) &i); 

输出:

 0x400634 0x7fffc1ef1a4c 

我知道他们存储在可执行文件的.rodata部分。 有没有一种特殊的方式操作系统后来处理它,所以文字最终在一个特殊的内存区域(前导零)? 这个内存位置有没有什么优点,或者有什么特别的地方呢?

以下是如何在Linux上布置进程内存(来自http://www.thegeekstuff.com/2012/03/linux-processes-memory-layout/ ):

Linux进程内存布局

.rodata部分是初始化全局数据块的写保护子部分。 ( ELF可执行文件指定.data的部分是其可写入的对应于可写全局变量的初始化为非零值的可写全局变量,初始化为0的可写全局变量转到.bss块,这里的全局变量是指全局变量和所有静态变量,而不考虑位置。

图片应该解释你的地址的数值。

如果您想进一步调查,那么在Linux上,您可以检查描述正在运行的进程的内存布局的/ proc / $ pid / maps虚拟文件。 你不会得到保留的(以点开头的)ELF段的名字,但是你可以通过查看它的内存保护标志来猜测哪个ELF段起源于一个内存块。 比如跑步

 $ cat /proc/self/maps #cat's memory map 

给我

 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] 

第一个r-xp块肯定来自.text (可执行代码),来自.rodata的第一个r--p块,以及来自.bss.data的以下rw块。 (在堆和堆栈块之间是由动态链接器从动态链接库加载的块)。


注意:为了符合这个标准,你应该把int*作为"%p"赋值给(void*) ,否则这个行为是不确定的。

这是因为字符串文字具有静态存储持续时间 。 也就是说,他们将在整个节目中生活。 这样的变量可以被存储在既不在所谓的堆也不在堆栈上的特殊存储器位置中。 因此地址的差异。

请记住,指针所在的位置与指针指向位置不同。 更现实的(苹果对苹果)比较会是

 printf ("%p\n", (void *) &h); printf ("%p\n", (void *) &i); 

我怀疑你会发现hp有类似的地址。 或者,另一个更现实的比较是

 static int si = 123; int *ip = &si; printf ("%p\n", (void *) h); printf ("%p\n", (void *) ip); 

我怀疑你会发现hip指向一个类似的内存区域。

 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. 

考虑文字是只读的变量,也有一个文字池的概念。 文字池是程序唯一文字的集合,其中重复的常量由于引用被合并为一个而被丢弃。

每个源都有一个文字池,根据链接/绑定程序的复杂程度,文字池可以彼此相邻放置以创建一个.rodata。

也不保证文字池是只读保护的。 语言虽然编译器设计如此。

考虑我的代码片段。 我本可以有

const char * cp =“hello world”;
const char * cp1 =“hello world”;

良好的编译器会认识到,在该源代码中,只读文字cp,cp1指向相同的字符串,并且将使cp1指向cp的文字,丢弃第二个文字。

还有一点。 文字池可能是256bytes或不同值的倍数。 如果池数据小于256字节,松弛将用十六进制零填充。

不同的编译器遵循通用的开发标准,允许用C编译的模块与用汇编语言或其他语言编译的模块链接。 这两个文字池连续放置在.rodata中。