字符串文字的C优化

bph*_*bph 22 c string gcc compiler-optimization string-pool

刚刚在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"};
Run Code Online (Sandbox Code Playgroud)

我得到以下内容:

(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"}
Run Code Online (Sandbox Code Playgroud)

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

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

Jos*_*fer 27

它被称为"字符串池".它在Microsoft Compilers中是可选的,但在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';
Run Code Online (Sandbox Code Playgroud)

  • 在GCC(至少4.7)中,禁用池的开关是-fno-merge-constants. (4认同)
  • @ dbrank0注意[gcc不再支持fwritabe-srings](https://gcc.gnu.org/gcc-4.0/changes.html),将这两个注释添加到你的答案中是理想的. (4认同)

AnT*_*AnT 8

(我假设你a,b,cd声明为局部变量,这是你的筹码相关预期的原因.)

C中的字符串文字具有静态存储持续时间.它们永远不会"堆叠".它们总是被分配在全局/静态存储器中并且"永久地"存在,即只要程序运行.

a,b,cd数组被分配在堆栈中.存储在这些数组中的指针指向静态内存.在这种情况下,对于相同单词的指针是完全相同的,没有什么不寻常之处.

编译器是否将相同的文字合并为一个取决于编译器.有些编译器甚至有一个控制此行为的选项.字符串文字总是只读的(这就是为什么最好const char *为数组使用类型),所以它们是否合并没有多大区别,直到你开始依赖实际的指针值.

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

  • 我所知道的任何内容都表明对同一个字符串文字*的两个(或更多)引用必须*解析到相同的内存位置.编译器可以(有些人)为每个字符串文字分配存储空间,即使有些是"重复".请参阅@Josh提到的"字符串池". (3认同)
  • 很棒的东西 - 这对我的理解有很大帮助,还没有完全理解字符串文字及其相关的存储持续时间 - 我错误地认为字符串只是堆栈上的局部变量(自动) (2认同)