为什么这两个指针相等?寻求澄清。

时间:2013-05-31 01:39:45

标签: c

我有一个简单的程序:

int main() {
    char *c = "message";
    char *z = "message";

    if ( c == z )
        printf("Equal!\n");
    else
        printf("Not equal!\n");
    return 0;
}

我想知道为什么这会打印Equal!,即使在关闭优化(-O0)的情况下进行编译也是如此。这表明cz都指向相同的内存区域,因此z的第一个突变(例如,将z[0]更改为a将是昂贵的(需要复制和写入)。

我对正在发生的事情的理解是,我声明一个类型为char的数组,而是创建一个指向string literal的第一个字符的指针。因此,cz都存储在数据段中,而不是存储在堆栈中(并且因为它们都指向相同的字符串文字,{{1} } 是真的)。

这与写作不同:

c == z

打印char c[] = "message"; char z[] = "message"; if ( c == z ) printf("Equal\n"); else printf("Not equal!\n"); ,因为Not equal!c都存储在内存的可变部分(即堆栈)中,并且分别存储在一个突变不会影响另一个。

我的问题是,我看到的行为(zc == z)定义的行为是什么?尽管未被声明为true,但char *c存储在数据段中似乎令人惊讶。

我尝试变异const定义的行为是什么?为什么,如果将char *z放入数据段并因此是只读的,为什么我得到char *c = "message"而不是编译器错误?例如,如果我这样做:

bus error

我明白了:

char *c = "message";
c[0] = 'a';
尽管它很快乐地编译。

进一步澄清这里发生的事情以及为什么会受到赞赏。

4 个答案:

答案 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”是一个常量,存储在只读存储器中,编译器只保留一个副本。我不知道最新的标准,但过去常常因编译器而异。