字符串文字的C类优化(C optimisation of string literals)

2019-06-14 18:17发布

刚刚被检查在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"}

我真的很惊讶,字符串指针是等效的话一样。 我本来以为每个字符串将被分配在栈上它自己的内存,无论它是否等同于另一个数组的字符串。

这是某种形式的编译器优化的例子还是对这种串申报标准的行为吗?

Answer 1:

这就是所谓的“字符串池”。 它是可选的微软编译器,但不是在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';


Answer 2:

(我假设你的abcd被声明为局部变量,这是你的筹码相关预期的原因。)

在C字符串文字具有静态存储持续时间。 他们从来没有“堆栈上的”分配。 他们总是在全局/静态存储器分配和生活“永远”,即只要程序运行。

abcd数组被分配在堆栈中。 存储在这些阵列中的指针指向静态存储器。 在这种情况下,没有什么不寻常的相同字是相同的指针。

无论是编译器将相同的文字合并成一个依赖于编译器。 一些编译器甚至有控制这种行为的选项。 字符串字面总是只读(这就是为什么它是一个更好的主意,用const char *您阵列式),所以它并没有太大的差别,他们是否合并与否,直到你开始依赖于实际的指针值。

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



文章来源: C optimisation of string literals