为什么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/ ):
.rodata部分是初始化全局数据块的写保护子部分。 ( ELF可执行文件指定.data的部分是其可写入的对应于可写全局variables的初始化为非零值的可写全局variables,初始化为0的可写全局variables转到.bss块,这里的全局variables是指全局variables和所有静态variables,而不考虑位置。
图片应该解释你的地址的数值。
如果您想进一步调查,那么在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块。 (在堆和堆栈块之间是由dynamic链接器从dynamic链接库加载的块)。
注意:为了符合这个标准,你应该把int*
作为"%p"
赋值给(void*)
,否则这个行为是不确定的。
这是因为string文字具有静态存储持续时间 。 也就是说,他们将在整个节目中生活。 这样的variables可以存储在既不在所谓的堆也不在堆栈上的特殊存储器位置中。 因此地址的差异。
请记住,指针所在的位置与指针指向的位置不同。 更现实的(苹果对苹果)比较会是
printf ("%p\n", (void *) &h); printf ("%p\n", (void *) &i);
我怀疑你会发现h
和p
有类似的地址。 或者,另一个更现实的比较是
static int si = 123; int *ip = &si; printf ("%p\n", (void *) h); printf ("%p\n", (void *) ip);
我怀疑你会发现h
和ip
指向一个类似的内存区域。
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.
考虑文字是只读的variables,也有一个文字池的概念。 文字池是程序唯一文字的集合,其中重复的常量由于引用被合并为一个而被丢弃。
每个源都有一个文字池,根据链接/绑定程序的复杂程度,文字池可以彼此相邻放置以创build一个.rodata。
也不保证文字池是只读保护的。 语言虽然编译器devise如此。
考虑我的代码片段。 我本可以有
const char * cp =“hello world”;
const char * cp1 =“hello world”;
良好的编译器会认识到,在该源代码中,只读文字cp,cp1指向相同的string,并且将使cp1指向cp的文字,丢弃第二个文字。
还有一点。 文字池可能是256bytes或不同值的倍数。 如果池数据小于256字节,松弛将用hex零填充。
不同的编译器遵循通用的开发标准,允许用C编译的模块与用汇编语言或其他语言编译的模块链接。 这两个文字池连续放置在.rodata中。