为什么关键字 false 在 gcc C23 中不是整数常量表达式?

Lun*_*din 31 c gcc null-pointer language-lawyer c23

gcc -std=c23最新的 gcc 13.x (trunk) 给出了此代码的编译器错误 ( ):

\n
int* p = false;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

错误:int *使用类型“ ”初始化类型“ _Bool”时出现类型不兼容

\n
\n

这怎么可能是正确的呢?

\n
\n

C23 6.2.5 \xc2\xa78-9(类型 - 整数类型的定义):

\n
\n

bool与标准有符号整数类型相对应的类型和无符号整数类型是标准无符号整数类型

\n

标准有符号整数类型和标准无符号整数类型统称为标准整数类型

\n
\n

C23 6.6 \xc2\xa78 (常量表达式 - 整型常量表达式的定义):

\n
\n

整数常量表达式应具有整数类型...

\n
\n

C23 6.3.2.3(指针-空指针常量的定义)

\n
\n

值为 0 的整型常量表达式,此类表达式转换为 类型void *,或预定义常量nullptr称为空指针常量

\n
\n

C23 6.5.16.1(简单分配):

\n
\n

约束
\n/--/

\n
    \n
  • 左操作数是原子、限定或非限定指针,右操作数是空指针常量
  • \n
\n
\n

结论:

\n
    \n
  • int* p = false;是有效的赋值形式,
  • \n
  • 因为false是一个空指针常量,
  • \n
  • 因为它是一个整数常量表达式,
  • \n
  • 因为它具有 type bool,这是标准整数类型之一。
  • \n
\n

我是否误解了标准中的任何内容,或者这是一个 gcc 错误?clang 发出警告,但仍然生成可执行文件。我也不明白 C23 中上述内容有何变化。

\n

包含stdbool.h并编译gcc -std=c17 -pedantic-errors使其能够按预期干净地编译。

\n

Joh*_*ger 30

除了问题中引用的段落外,还有第6.4.4.5/3段:

\n
\n

关键字falsetrue是类型常量bool,其值为\n0(对于 )false和 1(对于 )true

\n
\n

和 6.6/7:

\n
\n

标识符为:
\n[...]
\n\xe2\x80\x94 预定义常量;
\n[...]
\nis 是一个命名常量

\n
\n

这就形成了false一个命名常量,这是相关的,因为更完整的引用 6.6/8 是

\n
\n

整数常量表达式应具有整数类型,并且只能具有整数常量、整数类型的命名和复合文字常量[...]

\n
\n

(强调已添加)。

\n

那么,无论常量 周围的 GCC 实现细节如何false,当将其作为表达式时,false都满足 C23 对于整数常量表达式的标准。它的值被指定为 0。因此它是一个有效的空指针常量。

\n

GCC 的行为与语言规范相反。

\n
\n
\n

我也不太明白上述内容在 C23 中有何变化。

\n
\n

您提到的所有要点在 C23 中都没有改变,但改变的false和的类型true。在 C17 中,它们的类型为int。在 C23 中,它们有类型bool(又名_Bool)。然而,正如您所观察到的,bool它是整数类型,因此 GCC 不允许将其作为空指针常量的类型,这是不合格的。

\n

我注意到 GCC 8.5 也不接受_Bool空指针常量作为有效类型。鉴于此来源:

\n
void *test = (_Bool)0;\n\nint main(void) {\n}\n
Run Code Online (Sandbox Code Playgroud)\n

,它失败并出现与您观察到的相同错误:

\n
\n

npctest.c:1:14: 错误:使用类型 \xe2\x80\x98_Bool\xe2\x80\x99 初始化类型 \xe2\x80\x98void *\xe2\x80\x99 时类型不兼容

\n
\n

因此,您的观察结果似乎是一个更大、相对长期存在的问题的表现,该问题并非 C23 特有的。

\n

GCC 错误跟踪器中有关相关问题的评论中提到了此问题:https://gcc.gnu.org/bugzilla/show_bug.cgi ?id=112556 。

\n


dbu*_*ush 13

事实证明,该方式false是在 gcc 的 c2x 模式中定义的,它具有不同的类型。它仍然是一个整数常量表达式,但从 C23 开始,它具有 type_Bool而不是 type int

在 c2x 模式下将给定行传递给预处理器会得到以下结果:

int* p =  ((_Bool)+0u);
Run Code Online (Sandbox Code Playgroud)

C23的摘要提到应用了以下内容:

N2393 _Bool 定义 true 和 false。

文件规定以下内容:

  1. 参考实现 要为建议的更改添加最低限度的支持,实现必须将相当于以下几行的定义添加到其启动代码中:

    # define false (( bool ) +0)
    # define true (( bool ) +1)
    
    Run Code Online (Sandbox Code Playgroud)

所以C23的定义truefalse变化。

以上导致 gcc 至少返回到 4.9 时出现错误。

相反,预处理器在 c11 模式下输出以下内容:

int* p = 0;
Run Code Online (Sandbox Code Playgroud)

这是一个值为 0 的整数常量表达式。

有趣的是,以下内容在所有版本的 gcc 上编译时都会发出警告:

int x = 0;
int* p = x;
Run Code Online (Sandbox Code Playgroud)

输出:

int* p =  ((_Bool)+0u);
Run Code Online (Sandbox Code Playgroud)

虽然以下产生与上面相同的错误:

bool x = 0;
int* p = x;
Run Code Online (Sandbox Code Playgroud)

当然,上面不是常量表达式的情况,但它显示了 gcc 如何处理从int指针类型(而不是 )的转换_Bool

因此,考虑到整数到指针转换的规则,我想说这是 gcc 中的一个错误。

  • 请注意,在 C23 中,“false”和“true”是预定义常量(不是宏,除了可能作为实现细节的问题)。“stdbool.h”几乎被完全删除,特别是重新定义“false”和“true”的允许已被删除。n2393 中引用的位在这里并不真正相关,因为这些措辞都没有进入规范。它应该被视为有关现有实现如何采用更改的信息性评论,而不是这些常量语义的规范性描述。 (5认同)
  • “+”不会使表达式不是整数常量表达式。您引用的段落是关于允许哪些操作数,而不是关于允许哪些运算符,除了关于强制转换运算符的那句话。 (2认同)
  • 不管怎样,`int x = 0; int* p = x;` 不是有效的赋值形式之一,所以这是另一种情况。 (2认同)