关于C中的位掩蔽。为什么(~(0<;<;N))比((1<;<;N)-1)更可取?

关于C中的位掩蔽。为什么(~(0<;<;N))比((1<;<;N)-1)更可取?,c,bitmask,C,Bitmask,我确实知道~0将评估最大字长的位1s(因此需要考虑可移植性),但我仍然不明白为什么((1看看这些行: 1. printf("%X", ~(~0 << 31) ); 2. printf("%X", (1 << 31) - 1 ); 1.printf(“%X”,~(~0我不喜欢一个而不是另一个,但是我看到了(1EDIT)中的许多错误:更正了一个愚蠢的错误;并注意到了可能的溢出问题 我从来没有听说过一种形式比另一种更受欢迎。两种形式都是在编译时进行评估的。我总是使用第二种形式

我确实知道~0将评估最大字长的位1s(因此需要考虑可移植性),但我仍然不明白为什么((1看看这些行:

1. printf("%X", ~(~0 << 31) );
2. printf("%X", (1 << 31) - 1 );

1.printf(“%X”,~(~0我不喜欢一个而不是另一个,但是我看到了
(1EDIT)中的许多错误:更正了一个愚蠢的错误;并注意到了可能的溢出问题

我从来没有听说过一种形式比另一种更受欢迎。两种形式都是在编译时进行评估的。我总是使用第二种形式,我从来没有遇到过任何麻烦。读者对这两种形式都非常清楚

其他答复注意到第二种形式的溢出可能性


我看不出有什么可供选择的。

为什么要气馁
~0是单周期操作,因此速度更快
((1我不鼓励两者,对有符号值进行移位或补码操作是个坏主意。应始终在无符号类型和(如果必要)上生成位模式。)然后转置到有符号计数器部分。然后使用原语类型也不是一个好主意,因为通常在位模式上,您应该控制正在处理的位的数量

所以我总是做类似的事情

-UINT32_C(1)
~UINT32_C(0)
(UINT32_C(1) << N) - UINT32_C(1)
这是完全等效的,最后只使用
UINT32\u MAX
和Co

只有在您没有完全换档的情况下,换档才是必要的,例如

-UINT32_C(1)
~UINT32_C(0)
(UINT32_C(1) << N) - UINT32_C(1)

(UINT32_C(1)第一种形式肯定不是首选的,我甚至可以说它永远不应该被使用。在不支持负零的1补码系统上,
~0
很可能是陷阱表示,因此在使用时调用UB



另一方面,
1因为并非所有的C编译器/平台都使用2-补码来表示负数。@jv42为什么适用?我在那里没有看到负数,除了~0,但它的负数不需要工作。你能提供
的源代码吗?(1@harold:问题是如果你在(例如)中使用
unsigned int x=~0;
)1s'补码。
~0
是所有位的集合,作为表示负零(或陷阱表示)的有符号值。因此,当它转换为无符号时,结果是0,而不是
UINT_MAX
无符号int x=~0;
不考虑可移植性,如果“可移植性”是指“包括非2的s-补码”。正确的方法是
无符号整数x=-1;
无符号整数x=UINT_MAX;
@harold:没错,但我不是说2的补码。在不支持负零的1s补码实现中,
~0
是未定义的行为。因此严格一致的代码无法编写
~0
 ~0u
是可以的。同样的考虑也适用于
(1你可以做
(1u@Juraj Blaho:是的,但这不是问题。
((1谢谢你,丹尼斯!是的,它确实发出警告。)但是当用作位掩码时仍然不能忽略溢出?@MS:有符号溢出是UB,因此它可能工作,也可能不工作,这取决于编译器。Gcc基于有符号整数永远不应该溢出这一事实进行了积极的优化。@Rt
1如何在编译时计算第一个值而第二个值不能?难道
~0
也有32位的问题吗?@harold:~0将是32位的,如果int是32位的。但是你指的是什么问题呢?哦,可以理解,在
~0u>>(CHAR_-bit*sizeof(int)-N)
1我的意思是,这并没有给出选择的理由。(~0@harold:我只是指出了我在这种构造中遇到的一些问题。需要注意的是。我还想知道转换((1)这对我所知道的任何平台都是不正确的,但更重要的是,任何不是完全脑死亡的编译器都会使用零指令,只计算常数。哈罗德:什么是错误的?
在1中,移位和减法都不会消耗大量的周期。但在石器时代,移位超过1的速度通常很慢。这意味着逻辑移位和算术运算有同样的复杂性?我不这么认为:)我从来没有想到,'1事实上,
signed int
可以获取该范围内的所有值,这与
1这一事实无关,当时我似乎不太了解位移位操作。我曾经了解到,
C中的
1,
xI不相信这可以调用未定义的行为。C99 6.3.1.3§3
“否则,新类型将被签名,并且无法在其中表示值;要么结果是实现定义的,要么引发实现定义的信号。”