5

我有这样的代码:

$ cat test.c 
#include <stdio.h>
typedef struct
{
    const int x;
} SX;

static SX mksx(void)
{
    return (SX) { .x = 10 };
}

void fn(void)
{
    SX sx;
    while((sx = mksx()).x != 20)
    {
        printf("stupid code!");
    }
}

以及关于其正确性的 2 种意见:

$ for i in gcc clang; do echo "$i SAYS:"; $i -c -std=c99 -pedantic -Werror test.c; done
gcc SAYS:
test.c: In function ‘fn’:
test.c:15:2: error: assignment of read-only variable ‘sx’
  while((sx = mksx()).x != 20)
  ^
clang SAYS:

哪个编译器是对的?

4

2 回答 2

12

C99 标准在 6.5.16:2 中说:

赋值运算符应具有可修改的左值作为其左操作数。

在 6.3.2.1:1 中:

可修改的左值是没有数组类型、没有不完整类型、没有 const 限定类型,并且如果它是结构或联合,则没有任何成员(包括递归地,任何成员或所有包含的聚合或联合的元素)具有 const 限定类型

因此,GCC 发出警告是正确的。

此外,条款 6.5.16:2 位于 C99 标准的“约束”部分,因此需要符合标准的编译器来为破坏该条款的程序发出诊断。它仍然是未定义的行为:在发出诊断信息后,编译器仍然可以做它想做的事情。但必须有一个消息。因此,Clang 在这里的行为不符合规范。

于 2013-08-30T09:06:32.247 回答
3

const variable can't be modified after initialization, otherwise it's undefined behavior.

Since it is undefined behavior, I think one can say both gcc and clang follow the standard. (Although gcc's choice seems better, it deserves a warning) (See EDIT below)

The only way to give the variable x a value with defined behavior is to initialize it:

SX sx = { .x = 10 };

EDIT: As @Keith Thompson comments below, it's more than just undefined behavior in this case:

C99 §6.5.16 Assignment operators

Constraints

An assignment operator shall have a modifiable lvalue as its left operand.

This is a constraint, and according to:

C99 §5.1.1.3 Diagnostics

A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances.

A compiler must issue a diagnostic for any program that violates a constraint.

Back to the question, gcc is correct is generate a warning, while clang fails to do so.

于 2013-08-30T08:32:09.347 回答