语言标准一再提到对无符号整数的运算是模执行的2^n,其中n是表示无符号整数值所涉及的位数。
让我们有这个代码:
unsigned char a = 1;
a = -a;
而且,让unsigned char八位,但int十六位。
我已经认为表达式-a要么立即转换为类型的值unsigned char,等于255; 要么a扩展为int,然后这个int接受一个值-1,最后-1转换为类型unsigned char等于的值255。然而,事实证明,该标准指定了一种特殊的、相当奇怪的方法来计算无符号值的一元减号。
这是该文件第 8.3.1/8 段的引述:
一元 - 运算符的操作数应具有算术或无范围枚举类型,结果是其操作数的否定。对整数或枚举操作数执行整数提升。无符号量的负数是通过从 中减去其值来计算的
2^n,其中n是提升操作数中的位数。结果的类型是提升的操作数的类型。
如果我理解正确写的内容,那么-a上面示例中的表达式的值等于 value 2^n - 1,它n等于扩展操作数中的位数。那些。2^n - 1 == 2^16 - 1 == 65536 - 1 == 65535. 但是给定的值不能用 16 位表示int,这意味着尝试计算-a会导致未定义的行为。
问题是:一元减法可以像上面写的那样“起作用”吗?如果是这样,为什么标准会为无符号值定义这种奇怪的一元减法行为?
如果你这样写也很奇怪:
unsigned char a = 1;
a = 0 - a;
那么它似乎不会导致未定义的行为。因为它0是 type int,所以变量被a扩展为int,计算差值0-1,并且 type 的结果值被int安全地截断256为255。
关于 2 n的词仅适用于无符号类型(模算术
max() + 1- 结果始终是定义的)。这些词不是指 int 类型。如果
unsigned char一个类型的所有值都适合int(经常发生),那么(C++ n4659 §7.6.1):如果它们不适合,则:
其中
n是值的位数(值位 - 可能与sizeof * CHAR_BIT是否有填充位不同 - C n1570 §6.2.6.2.1)。也就是说,
-a表达式的类型是 int 或 unsigned int。要分配回a您需要(可能是缩小转换)到无符号字符:或者:
即在这种情况下总是获得该值 2 CHAR_BIT -1:
让我们定义文档中的概念:
第6.10条:
7.6积分促销
这一切的本质是在评估之后保留整个值范围,例如一元减号,泛型类型扩展
int。此操作(提升的操作数)的必要性由上下文决定。在代码中
泛化扩展可以省略,因为结果和它的存储位置都是一个字节,不需要额外的存储空间。这里
N=8,2^8=256,256-10=246. 和这里存储结果的类型
int,所以N=32,2^32-10=-10(在二进制补码中)。在8.3.1 一元运算符的第8 节中,声明该值
N不应取自源类型,而应取自最终类型,特别是,它可以是通用的——所有这些都基于上下文。耙子非常接近:
7.8 积分转换
7.9 浮点转换
因此,不应忽略有关尝试将有符号类型与无符号类型进行比较或隐式向下转换的编译器警告。