为什么C11全局和局部静态断言的行为不同?

问题描述 投票:0回答:2

请考虑以下代码:

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版本7.4.0中使用gcc -std=c11 -O3 test.c进行编译会产生指示的错误消息。也可以找到使用GCC 9.2的Compiler Explorer示例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的链接以帮助重现此问题。

请考虑以下代码:const int g_foo = 1; //(1):_Static_assert(g_foo> 0,“ g_foo> 0”); //错误:静态断言中的表达式不是常量。 _Static_assert(g_foo> 2,“ g_foo ...

c c11 static-assert
2个回答
4
投票

*(int *)&g_foo = -1;是明确的未定义行为,根据C17 6.7.3 / 6:


3
投票

因为它不起作用

© www.soinside.com 2019 - 2024. All rights reserved.