【发布时间】:2019-11-04 08:41:34
【问题描述】:
考虑以下代码:
const int g_foo = 1;
// (1):
_Static_assert(g_foo > 0, "g_foo > 0"); // error: expression in static assertion is not constant.
_Static_assert(g_foo > 2, "g_foo > 2"); // error: expression in static assertion is not constant.
void Foo(void) {
const int foo = 1;
// (2):
_Static_assert(foo > 0, "foo > 0"); // No issue.
_Static_assert(foo > 2, "foo > 2"); // error: static assertion failed: "foo > 2"
// (3):
_Static_assert(g_foo > 0, "g_foo > 0"); // No issue.
_Static_assert(g_foo > 2, "g_foo > 2"); // error: static assertion failed: "g_foo > 2"
}
使用gcc -std=c11 -O3 test.c 和 GCC 版本 7.4.0 进行编译会产生指示的错误消息。使用 GCC 9.2 的编译器资源管理器示例也可以在 here 找到。
从标有 (2) 的静态断言开始,我们使用 const 限定的局部变量。在这个优化级别,const int foo = 1; 被优化,因此初始化器不被评估。据我了解,这根据 ISO/IEC 9899:2011* 的 6.6.3 将其归类为常量表达式,并允许由_Static_assert** 对其进行评估。到目前为止一切顺利。
标有 (1) 的静态断言尝试评估包含全局变量 g_foo 的表达式,但全局变量在内存中分配空间这一事实意味着它们实际上需要被初始化。这种评估会自动取消它们成为常量表达式的资格,并且 GCC 会相应地抱怨。
然后我们到达 (3)。突然,在全局范围内失败的静态断言开始工作。怎么回事?
* 不幸的是,我使用的是2010-12-02 committee draft,而不是最终发布的规范。
** 有趣的是,在这个问题中使用非零优化级别很重要。使用-O0,变量foo 实际上是用文字1 实例化的。但是,这不再是一个常量表达式,并且使用 foo 的静态断言开始失败,并出现“静态断言中的表达式不是常量”。
编辑(2019-11-04):
- 从代码块中删除了
*(int *)&g_foo = -1;- 这是未定义的行为,会分散主要问题的注意力。 - 删除了关于在 C 中强制转换
const限定变量的多余俏皮话。 - 添加了指向 Compiler Explorer 的链接以帮助重现该问题。
【问题讨论】:
-
“然而,在 C 语言中,const 并不是那么强的语句” 它在定义中。您的分配会导致未定义的行为。
-
嗯,抛弃 const 并修改对象是明确未定义的行为 (port70.net/~nsz/c/c11/n1570.html#6.7.3p6)。对这样一个程序的推理主要是没有实际意义的。我建议你删除那一点,因为它并不是问题的真正主题,而且 UB 会减损一个有趣的案例研究。
-
“ *(int *)&g_foo = -1;”你为什么这样做?不要试图比编译器更聪明。
-
this classifies it as a constant-expression according to 6.6p3- 不。这都属于6.6.p10 -An implementation may accept other forms of constant expressions。 -
@user694733,等人:够公平的。
const分配是最后一分钟的补充,以强调静态断言的怪异。它是未定义的行为这一事实显然有损于重点,因此我已将其从问题中删除。
标签: c c11 static-assert