使用 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
形式上是一个 int
值 1
转换为 uint64_t
,而 1ul
是类型 [=] 的常量 1
16=] 这可能与 64 位系统上的 uint64_t
相同。由于您处理的是常量,因此所有计算都将由编译器完成,结果相同。
该宏是一种为 uint64_t
类型的常量(文字)指定正确后缀的可移植方法。宏附加的后缀(ul
,系统特定)只能用于文字常量。
强制转换(uint64_t)
可用于常量值和变量值。对于常量,它将与后缀或添加后缀的宏具有相同的效果,而对于不同类型的变量,它可能会执行值的截断或扩展(例如,从 32 更改时用 0 填充高位)位到 64 位)。
使用 UINT64_C(1)
还是 (uint64_t)1
是个人喜好问题。该宏使您更清楚地知道您正在处理一个常量。
如评论中所述,1ul
是 uint32_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
表达式中使用的常量。仅当常量必须具有特定类型时才需要它们,否则只需将常量拼写成不带后缀的十进制或十六进制就足够了。
例如,此代码已损坏(我刚刚在实际代码中修复了它..)。
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
形式上是一个 int
值 1
转换为 uint64_t
,而 1ul
是类型 [=] 的常量 1
16=] 这可能与 64 位系统上的 uint64_t
相同。由于您处理的是常量,因此所有计算都将由编译器完成,结果相同。
该宏是一种为 uint64_t
类型的常量(文字)指定正确后缀的可移植方法。宏附加的后缀(ul
,系统特定)只能用于文字常量。
强制转换(uint64_t)
可用于常量值和变量值。对于常量,它将与后缀或添加后缀的宏具有相同的效果,而对于不同类型的变量,它可能会执行值的截断或扩展(例如,从 32 更改时用 0 填充高位)位到 64 位)。
使用 UINT64_C(1)
还是 (uint64_t)1
是个人喜好问题。该宏使您更清楚地知道您正在处理一个常量。
如评论中所述,1ul
是 uint32_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
表达式中使用的常量。仅当常量必须具有特定类型时才需要它们,否则只需将常量拼写成不带后缀的十进制或十六进制就足够了。