有一个问题已经回答了变量声明的特殊情况,但其他文字常量用法呢?
例如:
uint64_t a;
...
int32_t b = a / 1000000000;
最后一段代码是否等同于任何标准C编译器中的下一段代码?
uint64_t a;
...
int32_t b = (int32_t)(a / UINT64_C(1000000000));
换句话说,是否需要xINTn_C宏(假设我们在隐式错误的情况下使用显式转换)?
修改
当编译器读取1000000000
时,是否允许将其作为int
存储在内部表示中(丢弃所有溢出的位),或者它必须以尽可能高的精度(long long)存储它,直到它结算为止整个表达类型?它是一个实现定义的行为还是由标准规定的?
答案 0 :(得分:4)
你的第二个例子是无效的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位,因此大到足以容纳10亿)。具有UINT64_C宏的第二个文字可能是unsigned long
或unsigned long long
,具体取决于平台。它将是与uint64_t
对应的任何类型。
所以常量的类型不一样。第一个是签名而第二个是未签名的。第二个很可能会有更多的“长”,这取决于编译器的基本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(值)应扩展为对应于uint_leastN_t类型的整数常量表达式。