如果您曾经使用第二种形式并遇到过问题,请分享一下。
看这些代码:
1. printf("%X", ~(~0 << 31) );
2. printf("%X", (1 << 31) - 1 );
第1
行编译并表现正常。
第2
行会提示警告信息:expression中的整数溢出。
这是因为默认情况下,1 << 31
被视为 带符号的 int 类型,所以 1 << 31 = -2147483648
,也就是最小的可能整数。
因此,对1
进行运算时会发生溢出。
(1u << 31) - 1
。 - Juraj Blaho((1 << N) - 1)
根本行不通。 - Dennis1 << 31
真是太可惜了。它是-2147483648
只是一个巧合。如果int
是32位宽,则1 << 31
的结果在int
中无法表示,这就是我们遇到的未定义行为。 - Jens Gustedt ~ 0
很可能是一个“trap representation”,因此在使用时会引发 UB。 1 << 31
也是 UB,假设 int
是 32 位,因为它会溢出。 0x7fffffff
是编写掩码的最简单和最正确的方法。如果你想要除了符号位以外的所有 int
, INT_MAX
是编写掩码的最简单和最正确的方式。 (1 << n) - 1
就是生成具有最低 n
个位设置的掩码的正确方法。最好使用 (1ULL << n)-1
然后进行强制转换或隐式转换,以免担心带符号问题和移位溢出。 ~
运算符。是未定义行为。直到今天,我读过的所有文本都说明
signed int可以取从
-2147483648到
2147483647的所有值,两者都包括在内。问题:
#define INT_MIN (-2147483647 - 1)是否可以解决这个问题?我问这个问题是因为这就是gcc、tcc和icl中
limits.h`所做的... - Dennissigned int
可以取该范围内的所有值,与 1<<31
溢出无关。这只是一个算术问题。2^31 大于 INT_MAX
(假设为 32 位 int),因此它会溢出。 - R.. GitHub STOP HELPING ICE1 << 31
而不是 2^31
,是将 1
左移 31 位并在右侧填充 0
。这将产生二进制的 10000000 00000000 00000000 00000000
或十六进制的 0x80000000
,或带符号整数的 -2147483648
。我错在哪里了? - Dennisx<<y
只是算术运算x*2^y
的简写。它与位移操作相对应只是一种巧合。 - R.. GitHub STOP HELPING ICE我会不鼓励在有符号值上进行移位或补码操作,因为这只是一个坏主意。应该总是在无符号类型上产生位模式,然后(如果必要)转置到有符号的对应部分。使用原始类型也不是一个好主意,因为通常在位模式上,你应该控制你正在处理的位数。
所以我总是会做一些像这样的事情
-UINT32_C(1)
~UINT32_C(0)
这些是完全等效的,最终只需要使用UINT32_MAX
等。
只有在您没有完全移位的情况下才需要移位,例如:
(UINT32_C(1) << N) - UINT32_C(1)
我不偏向于其中任何一个,但我见过许多使用(1<<N)
的错误,其中值必须为64位,但“1”是32位(int为32位),并且对于N>=31,结果是错误的。使用1ULL而不是1可以解决这个问题。这就是这种移位的一个危险之处。
此外,将int左移CHAR_BIT*sizeof(int)或更多位置(类似地,将long long(通常为64位)左移CHAR_BIT*sizeof(long long)或更多位置)是未定义的。因此,像这样向右移位可能更安全:~0u>>(CHAR_BIT*sizeof(int)-N)
,但在这种情况下,N不能为0。
~0
也有32位的问题吗? - harold中,
1 <= N <= CHAR_BIT * sizeof(int)`。您可以适当地将此表达式扩展到long-long。 - Alexey Frunze编辑:更正了一个愚蠢的错误,并注意到可能存在溢出问题。
我从未听说过一种形式比另一种形式更受欢迎。两种形式都在编译时评估。我总是使用第二种形式,而且从来没有遇到任何麻烦。对于读者来说,这两种形式都非常清晰明了。
其他答案指出了第二种形式可能存在溢出的可能性。
我认为它们之间几乎没有什么区别。
为什么不建议使用
~0是一个单周期操作,因此更快
((1<首先进行移位,然后进行减法,这是一种算术运算。由于减法,它将消耗大量的周期,因此会产生不必要的开销。
更多信息
而且,当你执行((1 << N)-1)或((M << N)-1)时,假设N指的是M的位数,因为它将刷新所有位。这里的1是整数,在几乎所有现有平台32/64位上都是32位,因此可以假定N为32。
但是,如果你将1强制转换为long并执行(((long)1 << 32) -1),结果将不同。 在这里,你需要使用64代替32,因为64是long的位数。
((1 << N) -1)
的来源吗? - Juraj Blahounsigned int x = ~0;
。~0
是所有位都设置为1,作为有符号值表示负零(或者是一个陷阱表示)。因此,当它被转换为无符号值时,结果是0,而不是UINT_MAX
。unsigned int x = ~0;
不能处理可移植性,如果我们的意思是“包括非2的补码”。正确的方法是unsigned int x = -1;
或unsigned int x = UINT_MAX;
。 - Steve Jessop~0
是未定义行为。因此,严格符合规范的代码不能写~0
。不过,~0u
是可以的。对于带符号表示法中的原码,(1 << N)
和 1's补码中的~0
一样糟糕,所以它们在有符号表示方面同样糟糕。 - Steve Jessop