字符串文字的C优化
刚刚在gdb中检查以下内容:
char *a[] = {"one","two","three","four"}; char *b[] = {"one","two","three","four"}; char *c[] = {"two","three","four","five"}; char *d[] = {"one","three","four","six"};
我得到以下内容:
(gdb) pa $17 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"} (gdb) pb $18 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"} (gdb) pc $19 = {0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four", 0x80961b7 "five"} (gdb) pd $20 = {0x80961a4 "one", 0x80961ac "three", 0x80961b2 "four", 0x80961bc "six"}
我真的很惊讶字符串指针对于相同的单词是相同的。 我原以为每个字符串都会在堆栈上分配自己的内存,无论它是否与另一个数组中的字符串相同。
这是某种编译器优化的一个例子,还是这种字符串声明的标准行为?
它被称为“字符串池”。 它在Microsoft Compilers中是可选的,但在GCC中不是。 如果在MSVC中关闭字符串池,则不同arrays中的“相同”字符串将被复制,并且具有不同的内存地址,因此会占用静态数据的额外(不必要的)50个字节。
编辑:gcc实际上有一个选项, -fwritable-strings
禁用字符串池。 此选项的效果有两个:它允许覆盖字符串文字,并禁用字符串池。 因此,在您的代码中,设置此标志将允许有些危险的代码
/* Overwrite the first string in a, so that it reads 'xne'. Does not */ /* affect the instances of the string "one" in b or d */ *a[0] = 'x';
(我假设你的a
, b
, c
和d
被声明为局部变量,这就是你的堆栈相关期望的原因。)
C中的字符串文字具有静态存储持续时间。 它们永远不会“堆叠”。 它们总是在全局/静态存储器中分配并且“永远”存在,即只要程序运行。
您的a
, b
, c
和d
数组已在堆栈上分配。 存储在这些数组中的指针指向静态内存。 在这种情况下,对于相同单词的指针是相同的,没有什么不寻常的。
编译器是否将相同的文字合并为一个取决于编译器。 有些编译器甚至有一个控制此行为的选项。 字符串文字总是只读的(这就是为什么最好为你的数组使用const char *
类型),所以它们是否合并没有多大区别,直到你开始依赖实际的指针值。
PS只是出于好奇:即使这些字符串文字是在堆栈上分配的,为什么你会期望相同的文字被多次“实例化”?