您的第二个示例不是有效的 C99,看起来像 C++。也许你想要的是一个演员,即(int32_t)(a / UINT64_C(1000000000))?
a / UINT64_C(1000000000) 和 a / 1000000000 有区别吗?不,他们最终会执行相同的操作。但我不认为这真的是你的问题。
我认为您的问题归结为整数文字“1000000000”的类型是什么?它是 int32_t 还是 int64_t? C99 中的答案来自 §6.4.4.1 第 5 段:
整数常量的类型是对应列表中第一个可以表示其值的类型。
对于没有后缀的十进制常量,列表为int、long int、long long int。所以第一个文字几乎肯定是int(取决于int 的大小,它可能是32 位,因此大到足以容纳十亿)。 UINT64_C 宏的第二个字面量可能是unsigned long 或unsigned long long,具体取决于平台。它将是与uint64_t 对应的任何类型。
所以常量的类型是不一样的。第一个将签名,而第二个未签名。第二个很可能有更多的“long”,具体取决于编译器的基本 int 类型的大小。
在您的示例中,文字具有不同的类型没有区别,因为 / 运算符需要将文字提升为 a 的类型(因为 a 的等级将等于或大于在任何情况下都是字面的)。这就是为什么我不认为这真的是你的问题。
关于为什么UINT64_C() 很重要的一个例子,考虑一个表达式,如果文字被提升为更大的类型,结果会发生变化。即,溢出将发生在文字的本机类型中。
int32_t a = 10;
uint64_t b = 1000000000 * a; // overflows 32-bits
uint64_t c = UINT64_C(1000000000) * a; // constant is 64-bit, no overflow
要计算 c,编译器需要将 a 提升为 uint64_t 并执行 64 位乘法。但是为了计算b,编译器将使用 32 位乘法,因为这两个值都是 32 位。
在最后一个示例中,可以使用强制转换代替宏:
uint64_t c = (uint_least64_t)(1000000000) * a;
这也将强制乘法至少为 64 位。
为什么你会使用宏而不是转换文字?一种可能性是因为十进制文字是有符号的。假设您想要一个不能表示为有符号值的常量?例如:
uint64_t x = (uint64_t)9888777666555444333; // warning, literal is too large
uint64_t y = UINT64_C(9888777666555444333); // works
uint64_t z = (uint64_t)(9888777666555444333U); // also works
另一种可能性是预处理器表达式。强制转换不是用于#if 指令表达式的合法语法。但是UINTxx_C() 宏是。
由于宏使用粘贴到文字上的后缀并且没有短后缀,因此可能会发现 UINT16_C(x) 和 UINT32_C(x) 是相同的。这给出了(uint_least16_t)(65537) != UINT16_C(65537) 的结果。不是人们所期望的。事实上,我很难看出这如何符合 C99 §7.18.4.1:
宏 UINTN_C(value) 应扩展为对应于类型 uint_leastN_t 的整数常量表达式。