使用 INTXX_C 宏和执行类型转换为文字有什么区别?

What is the difference between using INTXX_C macros and performing type cast to literals?

例如,此代码已损坏(我刚刚在实际代码中修复了它..)。

uint64_t a = 1 << 60

可以固定为,

uint64_t a = (uint64_t)1 << 60

但是这让我脑洞大开。

uint64_t a = UINT64_C(1) << 60

我知道 UINT64_C(1) 是一个在 64 位系统中通常扩展为 1ul 的宏,但是它与仅仅进行类型转换有什么不同呢?

(uint64_t)1 形式上是一个 int1 转换为 uint64_t,而 1ul 是类型 [=] 的常量 1 16=] 这可能与 64 位系统上的 uint64_t 相同。由于您处理的是常量,因此所有计算都将由编译器完成,结果相同。

该宏是一种为 uint64_t 类型的常量(文字)指定正确后缀的可移植方法。宏附加的后缀(ul,系统特定)只能用于文字常量。

强制转换(uint64_t) 可用于常量值和变量值。对于常量,它将与后缀或添加后缀的宏具有相同的效果,而对于不同类型的变量,它可能会执行值的截断或扩展(例如,从 32 更改时用 0 填充高位)位到 64 位)。

使用 UINT64_C(1) 还是 (uint64_t)1 是个人喜好问题。该宏使您更清楚地知道您正在处理一个常量。

如评论中所述,1uluint32_t,而不是 windows 系统上的 uint64_t。我希望宏 UINT64_C 将附加对应于 uint64_t 的特定于平台的后缀,因此在这种情况下它可能会附加 uLL。另见 .

没有明显的区别或优势,这些宏有点多余。演员和宏之间有一些细微的差别:

  • (uintn_t)1 用于预处理器目的可能很麻烦,而 UINTN_C(1) 扩展为单个 pp 令牌。

  • UINTN_C 的结果类型实际上是 uint_leastn_t 而不是 uintn_t。所以它不一定是你期望的类型。

  • 如果您在代码中键入 1 而不是 1u,那么针对 MISRA-C 等编码标准的静态分析器可能会抱怨,因为移动有符号整数不是一个好主意无论大小。
    (uint64_t)1u 符合 MISRA,UINT64_c(1) 可能不符合,或者至少分析器无法分辨,因为它不能像编译器那样扩展 pp 标记。 UINT64_C(1u) 可能不会工作,因为这个宏实现可能看起来像这样:

    #define UINT64_C(n) ((uint_least64_t) n ## ull)
    // BAD: 1u##ull = 1uull
    

一般来说,我建议使用显式转换。或者更好的是将所有这些包装在一个命名常量中:

#define MY_BIT ( (uint64_t)1u << 60 )

UINT64_C(1) 通过令牌粘贴生成单个令牌,而 ((uint64_t)1) 是具有相同值的常量表达式。

它们可以在发布的示例代码中互换使用,但不能在预处理器指令中使用,例如 #if 表达式。

XXX_C 宏应该用于定义可在 #if 表达式中使用的常量。仅当常量必须具有特定类型时才需要它们,否则只需将常量拼写成不带后缀的十进制或十六进制就足够了。