有一个问题已经回答了变量声明的特殊情况,但是其他文字常量的使用呢?
例如:
uint64_t a;
...
int32_t b = a / 1000000000;
Run Code Online (Sandbox Code Playgroud)
在任何标准 C 编译器中,最后一段代码是否等同于下一段代码?
uint64_t a;
...
int32_t b = (int32_t)(a / UINT64_C(1000000000));
Run Code Online (Sandbox Code Playgroud)
换句话说,是否需要 xINTn_C 宏(假设我们在隐式错误的情况下使用显式转换)?
编辑
当编译器读取 时1000000000,是否允许将其存储为int内部表示(删除所有溢出位),或者它必须以尽可能高的精度(long long)存储它,直到它解析整个表达式类型?它是实现定义的行为还是标准规定的?
你的第二个例子不是有效的 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。所以第一个文字几乎肯定是 an int(取决于an的大小int,它可能是 32 位,因此大到足以容纳 10 亿)。带有 UINT64_C 宏的第二个文字可能是 aunsigned long或unsigned long long,具体取决于平台。它将是对应于的任何类型uint64_t。
所以常量的类型是不一样的。第一个将被签名,而第二个是未签名的。第二个很可能有更多的“long”,这取决于基本 int 类型的编译器大小。
在您的示例中,文字具有不同的类型没有区别,因为/运算符需要将文字提升为 of 的类型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
Run Code Online (Sandbox Code Playgroud)
为了计算c,编译器将需要推动a到uint64_t并执行64位乘法。但是为了计算b编译器将使用 32 位乘法,因为两个值都是 32 位。
在最后一个示例中,可以使用强制转换而不是宏:
uint64_t c = (uint_least64_t)(1000000000) * a;
Run Code Online (Sandbox Code Playgroud)
这也将迫使乘法至少为 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
Run Code Online (Sandbox Code Playgroud)
另一种可能性是预处理器表达式。强制转换不是用于#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 的整数常量表达式。