我确实知道~0将评估最大字大小的位 1(因此需要考虑可移植性),但我仍然不明白为什么((1 << N) - 1)不鼓励?
如果您使用第二种形式并遇到任何麻烦,请分享。
看看这些行:
1. printf("%X", ~(~0 << 31) );
2. printf("%X", (1 << 31) - 1 );
Line1
编译并表现得像预期的那样。
Line在表达式中2
给出警告整数溢出。
这是因为1 << 31
默认情况下将其视为有符号整数,因此1 << 31 = -2147483648
,它是可能的最小整数。
结果,休息1
会导致溢出。
第一种形式绝对不是首选,我什至会说永远不应该使用它。在不支持负零的一个补码系统上,~0
很可能是一个陷阱表示,因此在使用时会调用 UB。
另一方面,1<<31
也是UB,假设int
是32位,因为它溢出了。
如果您真的将 31 表示为常数,那么这0x7fffffff
是编写掩码的最简单和最正确的方法。如果您想要除符号位之外的所有内容,则int
,INT_MAX
是编写掩码的最简单和最正确的方法。
只要您知道位移位不会溢出,(1<<n)-1
就是用最低n
位设置掩码的正确方法。最好使用(1ULL<<n)-1
后跟强制转换或隐式转换,以免担心符号问题和移位溢出。
但无论您做什么,都不~
要将运算符与有符号整数一起使用。曾经。
我会劝阻两者,对有符号值进行移位或补码操作只是一个坏主意。位模式应始终在无符号类型上生成,然后(如果必要)转置到有符号计数器部分。然后使用原始类型也不是一个好主意,因为通常在位模式上,您应该控制您正在处理的位数。
所以我总是会做类似的事情
-UINT32_C(1)
~UINT32_C(0)
它们是完全等价的,最后这只是为了使用UINT32_MAX
and Co.
仅在您没有完全换档的情况下才需要换档,例如
(UINT32_C(1) << N) - UINT32_C(1)
我不喜欢另一个,但是我看到了许多错误,(1<<N)
其中值必须是 64 位,但“1”是 32 位(整数是 32 位),结果对于 N>=31 是错误的. 1ULL 而不是 1 会修复它。这是这种转变的危险之一。
此外,未定义 CHAR_BIT*sizeof(int) 或更多位置的整数移位(对于 long long(通常为 64 位)由 CHAR_BIT*sizeof(long long) 或更多位置类似)。因此,像这样右移可能更安全:~0u>>(CHAR_BIT*sizeof(int)-N)
,但在这种情况下,N 不能为 0。
编辑:纠正了一个愚蠢的错误;并指出可能的溢出问题。
我从未听说过一种形式优于另一种形式。两种形式都在编译时进行评估。我总是使用第二种形式,而且我从来没有遇到任何麻烦。这两种形式对读者来说都是非常清楚的。
其他答案指出了第二种形式溢出的可能性。
我认为他们之间几乎没有选择。
为什么不鼓励〜0
是单周期操作,因此更快((1<首先进行移位,然后进行算术运算的减法。因此,由于减法,它将消耗大量周期,因此会消耗不必要的开销。
更
重要的是,当您执行 ((1 << N)-1) 或 ((M << N)-1) 相同时,假设 N 指的是 M 的位大小,因为它将刷新所有位。这里 1 是整数,通常是 32 位,目前几乎所有的平台都是 32/64 位,所以 N 可以假设为 32。
但是,如果您将 1 类型转换为 long 并执行 (((long)1 << 32) -1),则结果将不同。在这里你需要使用 64 代替 32,64 是 long 的大小。