我有一个简单的程序:
int main() {
char *c = "message";
char *z = "message";
if ( c == z )
printf("Equal!\n");
else
printf("Not equal!\n");
return 0;
}
我想知道为什么这会打印Equal!
,即使在关闭优化(-O0
)的情况下进行编译也是如此。这表明c
和z
都指向相同的内存区域,因此z
的第一个突变(例如,将z[0]
更改为a
将是昂贵的(需要复制和写入)。
我对正在发生的事情的理解是,我不声明一个类型为char
的数组,而是创建一个指向string literal
的第一个字符的指针。因此,c
和z
都存储在数据段中,而不是存储在堆栈中(并且因为它们都指向相同的字符串文字,{{1} } 是真的)。
这与写作不同:
c == z
打印char c[] = "message";
char z[] = "message";
if ( c == z ) printf("Equal\n");
else printf("Not equal!\n");
,因为Not equal!
和c
都存储在内存的可变部分(即堆栈)中,并且分别存储在一个突变不会影响另一个。
我的问题是,我看到的行为(z
为c == z
)定义的行为是什么?尽管未被声明为true
,但char *c
存储在数据段中似乎令人惊讶。
我尝试变异const
定义的行为是什么?为什么,如果将char *z
放入数据段并因此是只读的,为什么我得到char *c = "message"
而不是编译器错误?例如,如果我这样做:
bus error
我明白了:
char *c = "message";
c[0] = 'a';
尽管它很快乐地编译。
进一步澄清这里发生的事情以及为什么会受到赞赏。
答案 0 :(得分:7)
“z
的第一个变异(例如,将z[0]
更改为a
)将会很昂贵(需要复制和写入)。”
不“昂贵”;尝试 undefined 。字符串文字是常量。
答案 1 :(得分:2)
C 2011标准。第6.4.5节。字符串文字。第7段
如果这些数组[字符串文字]是不同的,只要它们的元素具有适当的值,则未指定。如果程序试图修改这样的数组,则行为是未定义的。
这意味着如果两个字符串文字具有相同的值,则允许编译器将它们指向内存中的相同位置,或者不同,但它只是编译器可以选择的选项。
答案 2 :(得分:1)
C编译器的一个步骤是在代码中找到一组所有字符串常量。它只存储任何不可变字符串的一个副本,即使该字符串在代码中存在两次。所以在你的例子中,你有"message"
两次 - 编译器将m e s s a g e \0
存储在文件中(在只读数据部分中),然后将这两个指针初始化为指向字符串
尝试使字符串不同,指针现在也应该不同。
另外,请尝试以下操作:打印您的可执行文件(如果它是a.out
,请运行cat -v a.out
)。您将看到字符串" message"," Equal!"和"不相等!"坐在可执行文件中。
更新:(已删除,因为它错了。)以下是生成的代码:
8048449: c7 44 24 1c 6d 65 73 73 movl $0x7373656d,0x1c(%esp)
8048451: c7 44 24 20 61 67 65 00 movl $0x656761,0x20(%esp)
8048459: c7 44 24 24 6d 65 73 73 movl $0x7373656d,0x24(%esp)
8048461: c7 44 24 28 61 67 65 00 movl $0x656761,0x28(%esp)
8048469: b8 70 85 04 08 mov $0x8048570,%eax
它创建了这个十六进制字符串两次(我的机器是little-endian):
6d 65 73 73 61 67 65 00
m e s s a g e \0
所以你是对的 - 它把字符串放在可变的内存中。
答案 3 :(得分:0)
文字“message”是一个常量,存储在只读存储器中,编译器只保留一个副本。我不知道最新的标准,但过去常常因编译器而异。