“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 分配给 ma​​sk 变量? ~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。对于宽于 unsignedu = ~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 &amp; sth == sth ~0 用于使所有位等于 1,无论使用何种平台 如果您确定您的计算机平台使用 2 的补码编码,您可以使用 -1 而不是 ~0

我个人的想法 - 让您的代码尽可能地独立于平台。成本相对较小,代码成为failproof

【讨论】:

仅当 1s 补码实现支持负零时。另见this answer

以上是关于“int mask = ~0;”的目的是啥?的主要内容,如果未能解决你的问题,请参考以下文章

AsQueryable() 的目的是啥?

@ConditionalOnProperty 注释的目的是啥?

toBeFalsy() 的目的是啥?

移位的目的是啥? [关闭]

docker build 输出的目的是啥?

partitioningBy的目的是啥