22

我刚刚在 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) p a
$17 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"}
(gdb) p b
$18 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"}
(gdb) p c
$19 = {0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four", 0x80961b7 "five"}
(gdb) p d
$20 = {0x80961a4 "one", 0x80961ac "three", 0x80961b2 "four", 0x80961bc "six"}

我真的很惊讶字符串指针对于等效单词是相同的。我原以为每个字符串都会在堆栈上分配自己的内存,而不管它是否与另一个数组中的字符串相同。

这是某种编译器优化的例子,还是这种字符串声明的标准行为?

4

2 回答 2

28

它被称为“字符串池”。它在 Microsoft 编译器中是可选的,但在 GCC 中不是。如果您在 MSVC 中关闭字符串池,那么不同数组中的“相同”字符串将被复制,并且具有不同的内存地址,因此会占用额外(不必要的)50 字节左右的静态数据。

编辑:v 4.0 之前的 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';
于 2012-07-09T17:06:26.323 回答
8

(我假设您的a,bcd声明为局部变量,这是您与堆栈相关的期望的原因。)

C 中的字符串文字具有静态存储持续时间。它们永远不会在“堆栈上”分配。它们总是分配在全局/静态内存中并且“永远”存在,即只要程序运行。

abcd数组在堆栈上分配。存储在这些数组中的指针指向静态内存。在这种情况下,相同单词的指针相同并没有什么不寻常的。

编译器是否会将相同的文字合并为一个取决于编译器。一些编译器甚至有一个选项来控制这种行为。字符串文字始终是只读的(这就是为什么const char *对数组使用类型是一个更好的主意),因此在您开始依赖实际指针值之前,它们是否被合并并没有太大区别。

PS只是出于好奇:即使这些字符串文字是在堆栈上分配的,为什么您会期望相同的文字多次“实例化”?

于 2012-07-09T16:58:04.703 回答