启用优化后 GCC 未报告结构存储大小错误

struct storage size error not reported by GCC with optimization turned on

在文件中 struct_test.c 我写:

static struct x x; 

我用 GCC 编译 (4.8.2, Ubuntu)

gcc -c -O0 struct_test.c

我得到了可以理解的错误:

error: storage size of 'x' isn't known

然后我重新编译,这次打开了优化

gcc -c -O struct_test.c

并且文件编译没有错误。

我有点理解为什么启用优化会消除错误,但有人可以帮我获得指示 gcc 忽略未定义标记的实际编译器选项吗?'x'?

当它出现在文件范围内时,此代码:

static struct x x;

x 声明为 struct x 类型的对象并具有内部链接。类型 struct x 之前不需要声明:

If a type specifier of the form struct-or-union identifier occurs other than [in a struct type declaration], and no other declaration of the identifier as a tag is visible, then it declares an incomplete structure or union type, and declares the identifier as the tag of that type.

(C99 6.7.2.3/8)

在翻译单元中首次遇到时不完整的结构或联合类型可以稍后在该翻译单元中完成:

A structure or union type of unknown content [...] is an incomplete type. It is completed, for all declarations of that type, by declaring the same structure or union tag with its defining content later in the same scope.

(C99 6.2.5/22)

C 通常区分指定对象类型的“声明”和指定对象类型并导致存储被保留的“定义”。在给定的翻译单元中,不是定义的声明可能具有不完整的类型,即 从未 在该单元内完成。

C99 的第 6.9.2 节指定哪些文件范围对象声明是定义:

  • 具有文件作用域和初始值设定项的声明是一个定义(具体来说,是一个“外部定义”,即使它定义的对象可能具有内部链接)
  • 具有文件作用域且没有初始化器的声明,并且具有静态存储 class 或没有存储 class 说明符是“暂定定义”。如果翻译单元不包含同一对象的外部定义,那么行为就好像有一个具有初始值设定项的外部定义 0.

本节还规定“如果对象的标识符声明为暂定定义且具有内部链接,则声明的类型不得为不完整类型。”这适用于此,但由于在翻译单元中某一点不完整的类型可以通过稍后的声明来完成,因此它不会固有地使相关代码行无效。但是,它 会使仅包含该行的翻译单元无效。

那么 GCC 怎么了?

由于给定的代码作为一个完整的翻译单元违反了“不得”的语言约束,因此它会产生未定义的行为。 GCC 没有义务拒绝代码或生成任何类型的诊断,尽管允许执行其中一个或两个。事实证明,无论优化级别如何,如果 -Wall-pedantic 标志打开,gcc 确实会发出有关代码的警告。无论如何,gcc 明确声明不作为验证代码是否符合标准的工具。

然而,这种特殊的违规行为相对温和。因为该对象从未被引用,gcc 可以假装它甚至从未被声明过,而不会冒错误或意外行为的风险。

-fdce(死代码消除)选项似乎会影响此特定行为。奇怪的是,尽管 -fdce 足以抑制优化级别 O0 的错误,但 -fno-dce 不会在更高的优化级别将其恢复。