(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)-1unsigned 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 。 这是在定义的宏,它根据需要自动附加UULULL 。 因此, 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双字等