为什么 "stdint.h" 的实现不同意 UINT8_C 的定义?

Why do implementations of "stdint.h" disagree on the definition of UINT8_C?

UINT8_C宏在"stdint.h"中定义,如下specificationUINTN_C(value)应扩展为整数常量表达式对应类型 uint_leastN_t.

然而,在野外,实现有所不同:

#define UINT8_C(value) ((uint8_t) __CONCAT(value, U))  // AVR-libc
#define UINT8_C(x_)    (static_cast<std::uint8_t>(x_)) // QP/C++
#define UINT8_C(c)     c                               // GNU C Library

前两个实现看起来大致相同,但第三个实现不同:例如,以下程序使用 AVR-libc 和 QP/C++ 打印 1,但 -1 使用 glibc(因为有符号值的右移会传播符号位)。

std::cout << (UINT8_C(-1) >> 7) << std::endl; // prints -1 in glibc

UINT16_C 的实现显示相同的行为,但 UINT32_C 则不同,因为它的定义包含 U 后缀:

#define UINT32_C(c) c ## U

有趣的是,glibc 的 UINT8_C 定义改变了 in 2006, due to a bug report。之前的定义是 #define UINT8_C(c) c ## U,但由于整数提升规则,在 -1 < UINT8_C(0) 上产生了不正确的输出 (false)。

根据标准,这三个定义都正确吗?这三种实现之间是否还有其他差异(除了负常量的处理之外)?

GNU C 库不正确。每 C11 7.20.4.1 Macros for minimum-width integer constants UINTN_C(value) 定义为

The macro UINTN_C(value) shall expand to an integer constant expression corresponding to the type uint_leastN_t.

所以他们只使用 c 是不合适的,因为 c 可能是也可能不是 uint_least8_t

前两个实现不符合 C 标准,因为它们不允许在 #if 指令中使用 UINT8_C(42):

#if UINT8_C(42) == 42 // <- should be a valid expression

N1570 7.20.4/3:

Each invocation of one of these macros shall expand to an integer constant expression suitable for use in #if preprocessing directives. The type of the expression shall have the same type as would an expression of the corresponding type converted according to the integer promotions. The value of the expression shall be that of the argument.

如果 int 可以表示 uint_least8_t 的所有值,那么 UINT8_C(value) 宏的 GNU 实现 #define UINT8_C(c) c 符合 C 标准。

根据C11 7.20.4 Macros for integer constants paragraph 2

The argument in any instance of these macros shall be an unsuffixed integer constant (as defined in 6.4.4.1) with a value that does not exceed the limits for the corresponding type.

例如,如果UINT_LEAST8_MAX是255,下面的用法示例是合法的:

  • UINT8_C(0)
  • UINT8_C(255)
  • UINT8_C(0377)
  • UINT8_C(0xff)

但以下用法示例会导致 未定义的行为:

  • UINT8_C(-1) — 不是 6.4.4.1
  • 中定义的整数常量
  • UINT8_C(1u) — 不是无后缀整数常量
  • UINT8_C(256) — 超过了此实施的 uint_least8_t 限制

出于同样的原因,带符号的等价物 INT8_C(-1) 也是 未定义的行为

如果 UINT_LEAST8_MAX 为 255,UINT8_C(value) 的合法实例将扩展为整数常量表达式,并且由于整数提升,其类型将为 int,如段落 3:

Each invocation of one of these macros shall expand to an integer constant expression suitable for use in #if preprocessing directives. The type of the expression shall have the same type as would an expression of the corresponding type converted according to the integer promotions. The value of the expression shall be that of the argument.

因此对于 UINT8_C(value) 的任何合法调用,通过 int 可以表示 uint_least8_t 的所有值的任何实现将其扩展为 value 是完美的符合标准。对于 UINT8_C(value) 的任何非法调用,由于 未定义的行为

,您可能无法获得预期的结果

[为完整性添加编辑] 正如 cpplearner's 中指出的那样,OP 问题中显示的 UINT8_C(value) 的其他实现无效,因为它们扩展为不适合在 [= 中使用的表达式27=] 处理指令。