string文字:他们去哪里?
我感兴趣的是在哪里string文字分配/存储。
我在这里find了一个有趣的答案,他说:
定义一个内联string实际上是embedded在程序本身的数据,不能改变(一些编译器允许这个聪明的把戏,不要打扰)。
但是,这与C ++有关,更不用说它不说了。
我很烦。 = d
所以我的问题是我的string文字保存在哪里以及如何? 为什么我不应该试图改变它? 实施是否因平台而异? 有没有人关心“巧妙的把戏”?
一个常见的技术是将string文字放在“只读数据”部分,该部分被映射到进程空间中为只读(这就是为什么您不能更改它)。
它确实因平台而异。 例如,更简单的芯片架构可能不支持只读存储器段,因此数据段将是可写的。
相反,然后尝试找出一个技巧,使string文字可变(这将高度依赖于您的平台,并可能随时间而改变),只是使用数组:
char foo[] = "...";
编译器将安排数组从文字初始化,您可以修改数组。
没有人回答这个问题。 C和C ++标准只是说,string文字具有静态存储持续时间,任何修改它们的尝试都会给出未定义的行为,并且具有相同内容的多个string文字可能共享或不共享相同的存储。
根据您要编写的系统以及它使用的可执行文件格式的function,可能会将它们与程序代码一起存储在文本段中,也可能有一个用于初始化数据的独立段。
确定细节也会根据平台而变化 – 最有可能包括可以告诉你它放在哪里的工具。 有些甚至会让你控制这样的细节,如果你想要的话(例如gnu ld允许你提供一个脚本来告诉它如何分组数据,代码等)
为什么我不应该试图改变它?
因为它是未定义的行为。 来自C99的报价N1256草稿6.7.8 / 32“初始化” :
例8:声明
char s[] = "abc", t[3] = "abc";
定义“简单的”字符数组对象
s
和t
其元素用string文字初始化。这个声明是一样的
char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };
数组的内容是可修改的。 另一方面,声明
char *p = "abc";
定义
p
的types为“指向char的指针”并将其初始化为指向长度为4的types为“char的数组”的对象,其元素用string文字初始化。 如果尝试使用p
来修改数组的内容,则行为是不确定的。
他们去哪里?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
-
char s[]
:堆栈 -
char *s
:-
.rodata
节的对象文件 - 对象文件的
.text
部分被转储的同一个段,具有Read和Exec权限,但不包含Write
-
程序:
#include <stdio.h> int main() { char *s = "abc"; printf("%s\n", s); return 0; }
编译和反编译:
gcc -ggdb -std=c99 -c main.c objdump -Sr main.o
输出包含:
char *s = "abc"; 8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp) f: 00 c: R_X86_64_32S .rodata
所以string存储在.rodata
节中。
然后:
readelf -l a.out
包含(简体):
Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x0000000000000704 0x0000000000000704 RE 200000 Section to Segment mapping: Segment Sections... 02 .text .rodata
这意味着默认链接脚本将.text
和.rodata
转储到可以执行但不能修改的段( Flags = RE
)。 尝试修改这样的段会导致Linux中发生段错误。
如果我们对char[]
做同样的处理:
char s[] = "abc";
我们获得:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
所以它被存储在堆栈中(相对于%rbp
),我们当然可以修改它。
仅供参考,只是备份其他答案:
标准: ISO / IEC 14882:2003说:
2.13。 string文字
[…]一个普通的string文字的types为“
n const char
数组”和静态存储时间(3.7)是否所有string文字都是不同的(即,是否存储在非重叠对象中)是由实现定义的。 尝试修改string文字的效果是未定义的。
gcc使得一个.rodata
节被映射到地址空间的某个地方并被标记为只读,
Visual C ++( cl.exe
)为同一目的制作.rdata
节。
您可以查看dumpbin
或objdump
(在Linux上)的输出以查看可执行文件的各个部分。
例如
>dumpbin vec1.exe Microsoft (R) COFF/PE Dumper Version 8.00.50727.762 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file vec1.exe File Type: EXECUTABLE IMAGE Summary 4000 .data 5000 .rdata <-- here are strings and other read-only stuff. 14000 .text
这取决于您的可执行文件的格式 。 一种考虑的方法是,如果你是汇编编程,你可能会把string文字放在汇编程序的数据段中。 你的C编译器做类似的事情,但这一切都取决于你正在编译的二进制文件系统。
string文字经常分配给只读存储器,使它们不可变。 然而,在一些编译器修改是可能的一个“聪明的把戏”。聪明的把戏是“使用字符指针指向内存”..记住一些编译器,可能不允许这..这里是演示
char *tabHeader = "Sound"; *tabHeader = 'L'; printf("%s\n",tabHeader); // Displays "Lound"
由于编译器和编译器可能有所不同,最好的方法是为search到的string文本过滤对象转储:
objdump -s main.o | grep -B 1 str
其中-s
强制objdump
显示所有段的全部内容, main.o
是对象文件, main.o
强制grep
在匹配之前也打印一行(这样就可以看到段名), str
是string你正在寻找的文字。
在Windows机器上使用gcc,并在main
声明一个variables
char *c = "whatever";
赛跑
objdump -s main.o | grep -B 1 whatever
回报
Contents of section .rdata: 0000 77686174 65766572 00000000 whatever....