10

宏在“stdint.h ”UINT8_C中定义,具有以下规范UINTN_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

前两个实现看起来大致相同,但第三个的行为不同:例如,以下程序1使用 AVR-libc 和 QP/C++ 打印,但-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 的定义在 2006 年UINT8_C发生了变化。之前的定义是,但是由于整数提升规则,它产生了不正确的输出 ( ) 。#define UINT8_C(c) c ## Ufalse-1 < UINT8_C(0)

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

4

3 回答 3

11

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

根据C11 7.20.4 Macros for integer constants2段:

这些宏的任何实例中的参数应该是一个无后缀的整数常量(如6.4.4.1中定义的),其值不超过相应类型的限制。

例如,如果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段:

这些宏之一的每次调用都应扩展为适合在#if预处理指令中使用的整数常量表达式。表达式的类型应与根据整数提升转换的相应类型的表达式具有相同的类型。表达式的值应该是参数的值。

因此,对于 的任何合法调用,通过任何可以表示 的所有值的实现UINT8_C(value)来扩展 this都是完全符合标准的。对于您的任何非法调用,由于未定义的行为,您可能无法获得您期望的结果。valueintuint_least8_tUINT8_C(value)

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

于 2019-08-08T18:40:26.760 回答
7

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

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

N1570 7.20.4/3

这些宏之一的每次调用都应扩展为适合在#if预处理指令中使用的整数常量表达式。表达式的类型应与根据整数提升转换的相应类型的表达式具有相同的类型。表达式的值应该是参数的值。

于 2019-08-08T18:18:59.847 回答
3

GNU C 库不正确。根据C11 7.20.4.1 最小宽度整数常量 UINTN_C(value)的宏定义为

UINTN_C(value)应扩展为与 type 对应的整数常量表达式uint_leastN_t

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

于 2019-08-08T16:48:42.423 回答