(uint64_t)-1是否保证产生0xffffffffffffffff?
我知道,C标准很好地定义了(unsigned)-1
必须产生2 ^ n-1,即一个无符号整数,其所有位都置位。 (uint64_t)-1ll
。 但是,我在C11标准中找不到指定如何解释(uint64_t)-1
内容。
所以,问题是:C标准是否有任何保证,以下哪项适用?
(uint64_t)-1 == (uint64_t)(unsigned)-1 //0x00000000ffffffff (uint64_t)-1 == (uint64_t)(int64_t)-1 //0xffffffffffffffff
是。 参见C11 6.3.1.3有符号和无符号整数:
1当具有整数类型的值转换为除_Bool之外的另一个整数类型时,如果该值可以由新类型表示,则它将保持不变。
2否则,如果新类型是无符号的,则通过重复加或减一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内。 60)
3否则,新类型已签名且值无法在其中表示; 结果是实现定义的,或者引发实现定义的信号。
60)规则描述了数学值的算术,而不是给定类型的表达式的值。
情况2适用,因此-1减少模0x10000000000000000以产生0xffffffffffffffff。
表达式1
和-1
类型为int
。 当转换为uint64_t
,加上或减去2 n直到该值在范围内的原则适用,因此结果总是2 n -1,在这种情况下n = 64。 因此, (uint64_t)-1
总是2 64 -1 ..
表达式(int64_t)-1
计算结果为-1,因此相同的推理适用于表达式(uint64_t)(int64_t)-1
,它也总是计算为2 64 -1。
另一方面, (unsigned)-1
是unsigned int
类型的正值,可以是2 16 -1,2 32 -1,2 64 -1或各种其他值,具体取决于编译平台。 转换为uint64_t
时,这些值可能不会产生2 64 -1。
我猜你在写(uint64_t)-1
而不是-1ULL
因为你不想对unsigned long long
的大小做出假设吗? 如果是这样,这很好。 但是,有一个尚未提及的替代方案(实际上并没有回答你的问题),但可以通过侧面提出问题来节省很多担心:
替代
一个好习惯是始终使用UINT64_C(x)
而不是(uint64_t)x
。 这是在
定义的宏,它根据需要自动附加U
, UL
或ULL
。 因此, UINT64_C(-1)
解析为-1U
, -1UL
或-1ULL
,具体取决于您的目标。 这保证始终正常工作。
铸造的危险
请注意, (uint64_t)x
实际上甚至不能正常工作。 例如,
(uint64_t)2147483648 // RISKY
在某些编译器上生成警告,因为值2147483648(2 ^ 31)太大而无法进入32位整数,并且以下内容甚至无法远程工作:
(uint64_t)1000000000000000000 // RISKY
但是,如果你使用UINT64_C()
,那么一切都是黄金的:
UINT64_C(2147483648) // GOOD UINT64_C(1000000000000000000) // GOOD UINT64_C(-1) // GOOD
笔记:
-
_C
后缀代表“常量”。 - 在
中,对于有符号和无符号值,还有8位,16位和32位版本。 - 对于-1的特殊情况,您也可以编写
UINT64_MAX
。
这是一个可以用几行代码回答的问题。
#include main() { int x; unsigned int y; x = -1; printf("\n0x%08x", x); y = (unsigned int) -1; printf("\n0x%08x", y); }
在Eclipse / Microsoft C编译器上运行此代码会产生:
0xffffffff 0xffffffff
类似的程序可以显示uint64_t
产生的内容。
最后,如果您了解计算机如何使用2的补数来添加数字,那么您将理解-1对于任意位数(8,32,64等)的单词总是对于单词中的每个字节都是ff双字等