“int mask = ~0;”的目的是啥?
Posted
技术标签:
【中文标题】“int mask = ~0;”的目的是啥?【英文标题】:What is the purpose of "int mask = ~0;"?“int mask = ~0;”的目的是什么? 【发布时间】:2018-03-04 17:45:42 【问题描述】:我在 C 中看到了以下代码行 here。
int mask = ~0;
我已经用 C 和 C++ 打印了 mask
的值。它总是打印-1
。
所以我确实有一些问题:
为什么要将值~0
分配给 mask 变量?
~0
的目的是什么?
我们可以使用-1
代替~0
吗?
【问题讨论】:
~0
在 2 的补码中只等于 -1
@PaulFloyd:链接的来源是关于一个纯粹的摆弄练习......就像举重一样有用
相关:Is it safe to use -1 to set all bits to true?
使用有符号类型作为掩码告诉我你的代码中发生了可怕的事情。
What does ~ operator do?的可能重复
【参考方案1】:
这是一种可移植的方式,可以将整数中的所有二进制位设置为 1 位,而无需知道当前架构中整数中有多少位。
【讨论】:
-1
还会在不知道int
类型的宽度的情况下将整数中的所有位设置为 1。它只是暗示使用二进制补码
@LưuVĩnhPhúc 正确。 ~0 方法具有较少的依赖关系。适用于无符号、非二的恭维系统,并且(可以说)不那么神秘。
@chqrlie 为什么不呢?顺便说一句,为位掩码使用有符号值是一个奇怪的想法。
@PeterJ_01:如果long
的位数多于int
,那么~0u
(类型为unsigned
)将作为零扩展的一部分初始化u
。
如果在一个补码上,它是不可移植的:它给出一个负零,符合标准的编译器可能会认为这是一个陷阱表示。那时你有UB。公平地说,这样的实现可以说首先没有 int
的全为值。【参考方案2】:
C 和 C++ 允许 3 种不同的有符号整数格式:符号幅度、一个补码和二进制补码
~0
将生成全一位无论系统使用的符号格式如何。所以它比-1
您可以添加U
后缀(即-1U
)以生成全一位模式可移植1。然而~0
表明意图更清晰:反转值 0 中的所有位,而 -1 将表明需要一个负一的值,而不是它的二进制表示
1 因为无符号操作总是reduced modulo the number that is one greater than the largest value that can be represented by the resulting type
【讨论】:
文中说可以假设2的补码32位整数 请注意,对于 all 无符号类型,u = -1;
将所有位设置为 1。对于宽于 unsigned
、u = ~0;
和 u = -1u;
的无符号类型不这样做。
【参考方案3】:
在 2 的补码平台上(假设)给你 -1,但规则禁止直接写 -1(仅整数 0..255、一元 !
、~
和二进制 &
、^
、|
、+
、<<
和 >>
是允许的)。
【讨论】:
【参考方案4】:您正在研究一项编码挑战,该挑战对执行给定任务的运算符和语言结构有许多限制。
第一个问题是在不使用-
运算符的情况下返回值-1。
在用二进制补码表示负数的机器上,值-1
表示为所有位设置为1
,因此~0
的计算结果为-1
:
/*
* minusOne - return a value of -1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 2
* Rating: 1
*/
int minusOne(void)
// ~0 = 111...111 = -1
return ~0;
文件中的其他问题并不总是正确实施。第二个问题,返回一个布尔值,表示 int
值将适合 16 位签名 short
有一个缺陷:
/*
* fitsShort - return 1 if x can be represented as a
* 16-bit, two's complement integer.
* Examples: fitsShort(33000) = 0, fitsShort(-32768) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 8
* Rating: 1
*/
int fitsShort(int x)
/*
* after left shift 16 and right shift 16, the left 16 of x is 00000..00 or 111...1111
* so after shift, if x remains the same, then it means that x can be represent as 16-bit
*/
return !(((x << 16) >> 16) ^ x);
左移负值或移值超出int
范围的数字具有未定义的行为,右移负值是实现定义的,因此上述解决方案不正确(尽管它可能是预期的解决方案) .
【讨论】:
【参考方案5】:很久以前,这就是您在极其有限的设备(例如 1K ZX 80 或 ZX 81 计算机)上节省内存的方法。在 BASIC 中,你会
Let X = NOT PI
而不是
LET X = 0
由于数字存储为 4 字节浮点数,因此后者比第一个 NOT PI 替代方案多占用 2 个字节,其中 NOT 和 PI 各占用一个字节。
【讨论】:
不完全是。在这些示例中,所有(关键字)字以及等号、变量名和 可见 零本身都由单个字节编码。但是,数字常量后跟(转义字节?)加上 4 字节浮点值,执行程序使用但未在源代码中显示。因此这里的大小差异是 3 甚至 4 个字节,而不是 2。 我应该在工作......只是在一些 ZX Spectrum 仿真器上做了这个并将其保存到磁带/磁盘。第一个使用 40 字节,第二个使用 35 个字节,因此相差 5 个字节。好吧,如果你在 ZX 81 上只有 1k,我想这可以节省 0.5% 的文件大小,这就是使用它的原因……回到 OP 问题——你能节省一两个字节吗(或 5)今天使用说 x = ~0 而不是 x = -1?也许使用诸如 long x = ~0 之类的转换? 我真的应该工作......只是在 c 中尝试了一些简单的方法,但是编译后的文件对于 -1、~0 和 ~(0x00) 的大小相同......我是肯定有一些架构可以节省一个字节左右,但现在当然必须不惜一切代价避免这种奇怪的事情。编译后的文件大约为 8.5k 字节。这会让可怜的ZX81心脏病发作...... 现代 C 编译器足够聪明,可以将~0
和 -1
识别为相同的值。他们还可以将代码优化为最大速度或最小大小,因此您必须使用编译选项。此外,编译后的代码可以四舍五入为 4、8 或 16 字节,以便更好地对齐函数的入口点,因此查看 obj
文件大小不适合您的目的。您可能宁愿将 C 代码编译为汇编,甚至反汇编已编译的对象模块(使用 objdump 之类的工具)以查看代码的逐字节表示。
这些系统上没有整数类型吗?而PI
怎么能存储在一个字节中呢?【参考方案6】:
在所有计算机体系结构中都有多种编码数字的方法。当使用 2 的补码时,这将始终为真:~0 == -1
。另一方面,一些计算机使用 1 的补码来编码上面的例子不正确的负数,因为~0 == -0
。是的,1s 补码有负零,这就是为什么它不是很直观。
所以你的问题
~0 被分配给掩码,因此掩码中的所有位都等于 1 -> 使mask & sth == sth
~0 用于使所有位等于 1,无论使用何种平台
如果您确定您的计算机平台使用 2 的补码编码,您可以使用 -1 而不是 ~0
我个人的想法 - 让您的代码尽可能地独立于平台。成本相对较小,代码成为failproof
【讨论】:
仅当 1s 补码实现支持负零时。另见this answer以上是关于“int mask = ~0;”的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章