2

K&R C(第二版,第 5.5 节)陈述如下(我强调):

char amessage[] = "a message"; /* an array */
char *pmessage = amessage; /* a pointer */

message是一个足够大的数组来保存字符序列和初始化它的 '\0' 。数组中的单个字符可能会更改,但message将始终引用相同的存储。OTOH,pmessage是一个指针,初始化为指向一个字符串常量;随后可以修改指针以指向别处;但如果您尝试修改字符串内容,则结果未定义

现在,我的问题是我的 Linux 机器上的 gcc 4.6.1(或 c99)是否应该在使用 -Wall 编译以下程序时生成警告:

int main(void) {
  char amessage[] = "a message";
  char *pmessage = "a message";
  pmessage[0] = 'b';
  return 0;
}

(我发现 gcc 不会产生任何警告。如果我正确解释上述内容,我的期望是它应该。)

4

4 回答 4

5

尽管您对 K&R 的引用是正确的,但我不希望这段代码生成警告,因为在一般情况下很难跟踪此类错误。

例如,考虑一段代码,其中您的指针最初指向一块可修改的内存,然后您对其进行一点操作,然后将其分配为指向字符串文字:

char [] ok = "quick brown fox";
char *ptr = ok;
for (int i = 0 ; i != 5 ; i++) {
    *ptr++ = '-'; // OK
}
ptr = "jumps over the lazy dog";
ptr[0] = 'J'; // Bad

对于编译器,跟踪这些分配并发出警告将非常棘手。覆盖所有情况是不可能的,因为指向字符串文字的指针可能来自外部链接的函数。

于 2013-06-26T00:50:12.977 回答
2

仅仅因为代码错误并不意味着 gcc 可以为其生成警告。如果可以在编译时检测到所有未定义行为的实例,则没有理由使行为未定义。它可能只是一个强制性错误,或者编译为等效abort();或类似的错误。

我同意 gcc 在这种情况下很容易生成警告。但是关于:

int main(void) {
  char amessage[] = "a message";
  char *pmessage = "a message";
  if (is_prime(some_constant_with_100_million_digits))
      pmessage = amessage;
  pmessage[0] = 'b';
  return 0;
}

它会调用UB吗?

于 2013-06-26T00:50:04.980 回答
0

它可能不会发出警告,因为它是未定义的。

同样,我可以这样做:

char amessage[] = "a message";
char *pmessage = amessage;
free(pmessage);

它不会发出警告,但应该会出现段错误。

我认为警告是针对已定义的行为。

于 2013-06-26T00:52:06.663 回答
0

好吧,它不会说什么,因为pmessage它不是指向 const 的指针,而且我想在这种情况下它也没有费心提及它,因为它还没有进行足够的静态分析(你可以尝试-O切换,看看是否会带来更多洞察力),或者因为在这种情况下没有人让它说什么——这通常意味着没有机会对未定义的行为进行不直观的优化。

GCC 支持-Wwrite-strings,这将在初始化时引发问题,迫使您创建pmessage一个指向 const 的指针,随后使以下行非法。

于 2013-06-27T16:12:06.683 回答