5

我有一个简单的程序:

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)的第一个突变将是昂贵的(需要复制和写入)。

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

这与写作不同:

char c[] = "message";
char z[] = "message";

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

打印Not equal!, 因为cz都存储在内存的可变部分(即堆栈)中,并且分别存储,因此一个突变不会影响另一个。

我的问题是,我看到的行为(c == zas true)是定义的行为吗?char *c尽管没有被声明为 ,但 存储在数据段中似乎令人惊讶const

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

char *c = "message";
c[0] = 'a';

我得到:

zsh: bus error  ./a.out

虽然它编译得很愉快。

任何进一步澄清这里发生的事情以及原因将不胜感激。

4

4 回答 4

7

z(例如,更改z[0]a)的第一个突变将是昂贵的(需要复制和写入)。”

不贵啊”; 尝试未定义。字符串文字是常量。

于 2013-05-31T01:41:48.843 回答
2

C 2011 标准。第 6.4.5 节。字符串字面量。第 7 段

如果它们的元素具有适当的值,则未指定这些数组 [ string literals ] 是否不同。如果程序尝试修改这样的数组,则行为未定义。

这意味着如果两个字符串文字具有相同的值,则允许编译器让它们指向内存中的相同位置或不同位置,但这只是编译器可以做出的选择。

于 2013-05-31T04:16:51.667 回答
1

C 编译器的一个步骤是在代码中找到一组所有字符串常量。它只会存储任何不可变字符串的一份副本,即使该字符串在代码中存在两次。因此,在您的示例中,您有"message"两次 - 编译器将存储m e s s a g e \0在文件中(在只读数据部分中),然后将初始化这两个指针以指向字符串。

尝试使字符串不同,指针现在也应该不同。

另外,试试这个:打印你的可执行文件(如果它是 call a.out, run cat -v a.out)。您将看到字符串“消息”、“相等!” 和“不相等!” 坐在可执行文件中。

更新:(删除,因为它是错误的。)这是生成的代码:

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

所以你是对的——它将字符串放入可变内存中。

于 2013-05-31T01:46:11.553 回答
0

文字“消息”是一个常量,存储在只读内存中,编译器只保留一份副本。我不知道最新的标准,但这曾经因编译器而异。

于 2013-05-31T01:46:39.087 回答