【问题标题】:Which C99-compiler (Clang vs. GCC) is closer to standard on const structure fields?哪个 C99 编译器(Clang 与 GCC)更接近 const 结构字段的标准?
【发布时间】:2017-07-02 15:40:57
【问题描述】:

我有这样的代码:

$ 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:

哪个编译器是正确的?

【问题讨论】:

  • 我会说你应该考虑 GCC,它被广泛使用(甚至最新的 GCC 4.8.1 也会出现同样的错误)。这个务实的理由应该足以避免这样的编码。
  • 他可能只是想知道要针对哪个编译器提交错误报告。 (同样有效的答案可能是“都不是”)

标签: c gcc constants clang c99


【解决方案1】:

C99 标准在 6.5.16:2 中说:

赋值运算符应该有一个可修改的左值作为它的左操作数。

在 6.3.2.1:1 中:

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

所以 GCC 发出警告是对的。

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

【讨论】:

  • 6.5.15:2 是违反约束的,而不仅仅是未定义的行为。符合标准的编译器必须对任何违反约束的程序发出诊断 (C99 5.1.3.3); clang 没有这样做。
  • @KeithThompson 当我正在输入那部分时,我想“Keith Thompson 会检查我们是否在这里谈论约束违规”。我不知何故设法忽略了这个词。
  • 我想知道这个“功能”的预期目的是什么?在其他一些语言(如 C#)中,允许在结构成员上使用“只读”限定符,限定符表示不会通过覆盖整个结构来更改成员 except;这样的构造对于应该支持某些不变量的结构很有用(通常会使用返回适当初始化结构的函数来构造实例)。对于具有 C99 语义的 const 限定字段的 C 结构,我想不出任何用例。
【解决方案2】:

const变量初始化后不能修改,否则为未定义行为。

由于它是未定义的行为,我认为可以说 gcc 和 clang 都遵循标准。 (虽然 gcc 的选择似乎更好,但值得警告)(参见下面的EDIT

为变量x 赋予具有已定义行为的值的唯一方法是对其进行初始化:

SX sx = { .x = 10 };

编辑:正如下面的@Keith Thompson cmets,在这种情况下,它不仅仅是未定义的行为:

C99 §6.5.16 赋值运算符

约束

赋值运算符应该有一个可修改的左值作为它的左操作数。

这是一个约束,根据:

C99 §5.1.1.3 诊断

一个符合要求的实现应产生至少一个诊断消息(在 实现定义的方式)如果是预处理翻译单元或翻译单元 包含违反任何语法规则或约束的行为,即使该行为也明确指定为未定义或实现定义。在其他情况下不需要生成诊断消息。

编译器必须为任何违反约束的程序发出诊断。

回到问题,gcc 是正确的就是产生一个警告,而 clang 没有这样做。

【讨论】:

  • 为什么要使用复合文字进行初始化。 SX sx = { .x = 10 }; 应该也能正常工作
  • @JensGustedt 是的,我只是按照问题代码中使用的 OP 进行操作。
  • 这不仅仅是未定义的行为;这是违反约束的,需要任何符合要求的编译器进行诊断。请参阅Pascal's answer 和我的评论。
猜你喜欢
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 2016-06-09
  • 2014-04-12
  • 1970-01-01
  • 1970-01-01
  • 2013-07-28
相关资源
最近更新 更多