C11_Generic 将 true 和 false 推导为整数

C11_Generic deduces true and false as integers

在 C11 中,有一个 _Generic 宏可以实现很酷的泛型函数。然而,在正常情况下使用 truefalse 会导致不正确的推导:

#include <stdio.h>
#include <stdbool.h>

#define TypeName(x) \
  _Generic((x), \
    bool: "bool", \
    int: "int", \
    default: "unknown")

#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && (__bool_true_false_are_defined)
# undef true
# define true ((bool)(1))
# undef false
# define false ((bool)(0))
#endif

int main(void)
{
    printf("1: %s\n", TypeName(1));
    printf("true: %s\n", TypeName(true));
    printf("false: %s\n", TypeName(false));
}

这会打印:

1: int
true: bool
false: bool

但是没有重新定义truefalse的中间位:

1: int
true: int
false: int

这意味着您不能执行 _Generic 功能,例如:

struct Variant * const int32 = variant_create(1);
struct Variant * const boolean = variant_create(true);

所以我的问题是:

那是因为stdbool.h中的truefalse只是整数10,它们的类型确实是int,不是bool.

C11 §7.18 Boolean type and values <stdbool.h>

The remaining three macros are suitable for use in #if preprocessing directives. They are

true

which expands to the integer constant 1,

false

which expands to the integer constant 0, and

__bool_true_false_are_defined

which expands to the integer constant 1.

两种类型确实都是宏:

7.18 Boolean type and values

  1. The remaining three macros are suitable for use in #if preprocessing directives.
    They are:
    true which expands to the integer constant 1,
    false which expands to the integer constant 0,
    and
    __bool_true_false_are_defined which expands to the integer constant 1.

最后一条规则说你可以重新定义宏:

  1. Notwithstanding the provisions of 7.1.3, a program may undefine and perhaps then redefine the macros bool, true, and false. 259)

    259) See ‘‘future library directions’’ (7.31.9)

尽管引用了规则:

7.1.3 Reserved identifiers

  1. If the program removes (with #undef) any macro definition of an identifier in the first group listed above, the behavior is undefined.

规则 7.31.9 说重新定义可能不是一个好主意:

7.31.9 Boolean type and values

  1. The ability to undefine and perhaps then redefine the macros bool, true, and false is an obsolescent feature.

所以我建议您创建自己的 my_true 和 my_false 宏,这些宏被转换为 _Bool。

你的问题 "Is this an oversight in the C11 standard or a bug in GCC and Clang?" 的答案是 "neither"。

其他人已经解释了为什么它不是 GCC 和 Clang 中的错误。

这也不是标准中的疏忽,标准定义了 truefalse 以实现向后兼容性。如果 true 被定义为 (bool)1 并且 false 被定义为 (bool) 0,更容易遇到不兼容的宏定义。最明显的效果是警告。如果宏的重新定义(不干预#undef)没有改变定义,编译器通常不会发出警告;因此,最简单的定义最不可能在实际中产生问题。

C 没有比 int 更窄的整数类型的文字,正如其他人所说,宏 falsetrue 因此被定义为 01。因此,您为 _Bool 描述的问题对于所有其他窄数据类型都是相同的:很难在类型泛型宏中触发例如 short 变体,并使用 [=18 调用这样的宏=] 也不会触发 char 变体。

您的替换宏不好用,因为它们在预处理器 #if 表达式中不可用。如果您之后包含的某些代码使用它们,那么编译将失败。

你可以使用

# define true ((_Bool)+1)
# define false ((_Bool)+0)

相反。小加号 + 神奇地使预处理器将它们解释为表达式中的 ((0)+1)((0)+0),因此它们仍然会按照您的预期进行。