C 字符串文字:它们到哪里去了?
我对字符串文本的分配/存储位置感兴趣 我确实找到了一个有趣的答案,说: 内联定义字符串实际上将数据嵌入到程序本身中,并且无法更改(一些编译器通过巧妙的技巧允许这样做,不用麻烦) <>但是,它与C++有关,更不用说它不麻烦了。 我很烦=DC 字符串文字:它们到哪里去了?,c,memory,string-literals,C,Memory,String Literals,我对字符串文本的分配/存储位置感兴趣 我确实找到了一个有趣的答案,说: 内联定义字符串实际上将数据嵌入到程序本身中,并且无法更改(一些编译器通过巧妙的技巧允许这样做,不用麻烦) 但是,它与C++有关,更不用说它不麻烦了。 我很烦=D 所以我的问题是我的字符串文字保存在哪里以及如何保存?为什么我不尝试改变它?实现是否因平台而异?有人想详细介绍一下“智能技巧”吗?一种常见的技术是将字符串文本放入“只读数据”部分,该部分以只读形式映射到进程空间中(这就是为什么您不能更改它) 它确实因平台而异。例如,较
所以我的问题是我的字符串文字保存在哪里以及如何保存?为什么我不尝试改变它?实现是否因平台而异?有人想详细介绍一下“智能技巧”吗?一种常见的技术是将字符串文本放入“只读数据”部分,该部分以只读形式映射到进程空间中(这就是为什么您不能更改它) 它确实因平台而异。例如,较简单的芯片架构可能不支持只读存储器段,因此数据段将是可写的 与其尝试找出让字符串文本可变的技巧(它将高度依赖于您的平台,并且可能随时间而变化),不如使用数组:
char foo[] = "...";
编译器将安排从文本中初始化数组,您可以修改数组。这取决于您的语言。考虑这一点的一种方法是,如果您正在进行汇编编程,您可能会将字符串文本放入汇编程序的数据段中。您的C编译器会执行类似的操作,但这完全取决于您的二进制代码是为哪个系统编译的。对此没有一个答案。C和C++标准只是说字符串文本具有静态存储持续时间,任何修改它们的尝试都会给出未定义的行为,并且具有相同内容的多个字符串文字可能共享或可能不共享相同的存储。 根据您为之编写的系统及其使用的可执行文件格式的功能,它们可能与程序代码一起存储在文本段中,也可能有单独的用于初始化数据的段
根据平台的不同,确定细节也会有所不同——很可能包括可以告诉您其放置位置的工具。如果您需要,有些甚至可以让您控制这样的细节(例如,gnu ld允许您提供一个脚本,告诉您如何对数据、代码等进行分组)。gcc制作一个
.rodata
部分,该部分映射到地址空间中的“某处”,并标记为只读
VisualC++(<代码> C.exe < /Cuff>)生成了<代码> .RDATA< /Cuff>节,用于相同的目的。
您可以查看dumpbin
或objdump
(在Linux上)的输出,以查看可执行文件的部分
例如
>dumpbin vec1.exe
Microsoft(R)COFF/PE转储程序版本8.00.50727.762
版权所有(C)微软公司。版权所有。
文件vec1.exe的转储
文件类型:可执行映像
总结
4000.数据
5000.rdataFYI,仅支持其他答案:
标准:说明:
2.13。字符串文本
[…]普通字符串文字具有类型“array ofn const char
”和
静态存储持续时间(3.7)
是否所有字符串文字都是不同的(即存储在
非重叠对象)是
实现定义。影响
正在尝试修改字符串文字
没有定义
字符串文本经常分配给只读内存,使其不可变。然而,在某些编译器中,可以通过“智能技巧”进行修改。智能技巧是通过“使用指向内存的字符指针”。记住,某些编译器可能不允许这样做。下面是演示
char *tabHeader = "Sound";
*tabHeader = 'L';
printf("%s\n",tabHeader); // Displays "Lound"
我为什么不尝试更改它?
因为它是未定义的行为。引用自6.7.8/32“初始化”:
例8:声明
char s[] = "abc", t[3] = "abc";
char *p = "abc";
定义“普通”字符数组对象s
和t
,它们的元素用字符串文字初始化
此声明与
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
数组的内容是可修改的。另一方面,宣言
char s[] = "abc", t[3] = "abc";
char *p = "abc";
使用类型“pointer to char”定义p
,并将其初始化为指向长度为4的类型为“array of char”的对象,该对象的元素使用字符串文字初始化。如果试图使用p
修改数组的内容,则行为未定义
他们去哪里?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
chars[]
:堆栈
char*s
:
- 对象文件的
.rodata
部分
- 转储对象文件的
.text
部分的同一段,该段具有读取和执行权限,但不具有写入权限
节目:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
输出包含:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
因此字符串存储在.rodata
部分
然后:
包含(简化):
这意味着默认链接器脚本将.text
和.rodata
转储到一个可以执行但不能修改的段中(Flags=re
)。试图修改这样一个段会导致Linux中的SEGFULT
如果我们对char[]
执行相同的操作:
char s[] = "abc";
我们获得:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
因此,它存储在堆栈中(相对于%rbp
),我们当然可以修改它。由于这可能因编译器而异,最好的方法是过滤对象转储以查找搜索的字符串文字:
objdump -s main.o | grep -B 1 str
其中,-s
强制objdump
显示所有节的全部内容,main.o
是目标文件,-b1
强制grep
也在匹配前打印一行(以便您可以看到节名),str
是您正在搜索的字符串文本
在Windows计算机上使用gcc,并且在main
like中声明一个变量
char *c = "whatever";
运行
objdump -s main.o | grep -B 1 whatever
char *c = "whatever";
objdump -s main.o | grep -B 1 whatever
Contents of section .rdata:
0000 77686174 65766572 00000000 whatever....