我在C语言中看到了以下这行代码(链接)。
int mask = ~0;
我已经在C和C++中打印了mask
的值,它总是打印出-1
。
因此我有一些问题:
- 为什么要给mask变量赋值
~0
? ~0
的目的是什么?- 我们可以用
-1
代替~0
吗?
这是一种便携式的方法,可以将整数中的所有二进制位设置为1位,而无需知道当前架构中整数的位数。
-1
会将一个整数中的所有位设置为1,而不需要知道int
类型的宽度。这只是暗示使用二进制补码。 - phuclvlong
比int
有更多的位数,那么~0u
(类型为unsigned
)将作为初始化u
的一部分被_零扩展_。 - hmakholm left over Monicaint
类型的全 1 值。 - KevinC和C++支持三种不同的有符号整数格式:原码、反码和补码
~0
会产生所有位都是1的结果,无论系统使用哪种符号格式。因此,它比-1
更具可移植性
你可以添加U
后缀(例如-1U
)来可移植地1生成全为1的位模式。然而,~0
更清晰地表明了意图: 反转值0中的所有位,而-1将显示需要一个负一的值,而不是它的二进制表示。
1因为无符号操作总是取模于比该类型所能表示的最大值还要大一的数字
在使用2的补码平台时(假设),-1是表示为全1,但根据规则,直接写-1是不允许的(只允许使用整数0..255、一元运算符!
、~
和二元运算符&
、^
、|
、+
、<<
和>>
)。
/*
* 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
范围的数字向左移位具有未定义的行为,右移动负值是实现定义的,因此上述解决方案是不正确的(尽管它可能是预期的解决方案)。
Let X = NOT PI
LET X = 0
由于数字被存储为4字节浮点数,后者比第一个NOT PI替代方案多占用了2个字节,其中每个NOT和PI占用一个字节。
~0
和-1
识别为相同的值。它们还可以将代码优化到最大速度或最小尺寸,因此您需要尝试不同的编译选项。此外,编译后的代码可以舍入为4、8或16字节,以更好地对齐函数的入口点,因此查看obj
文件大小并不适合您的目的。您可能更愿意将C代码编译为汇编代码,甚至反汇编已编译的对象模块(使用类似objdump的工具)以查看代码的逐字节表示。 - CiaPanPI
如何存储在单个字节中? - phuclv在所有计算机架构中,有多种编码数字的方式。当使用2的补码时,这将始终成立:~0 == -1
。另一方面,一些计算机使用1的补码来编码负数,对于这些情况,上述示例不成立,因为~0 == -0
。是的,1的补码有负零,这就是为什么它不太直观。
所以回答你的问题:
mask & sth == sth
我的个人想法-尽可能使您的代码与平台无关。成本相对较小,代码变得无故障。
~0
等同于-1
。 - phuclv