为什么C ++标准指定有符号整数在具有混合签名的二进制操作中转换为无符号?
C和C ++标准规定,在相同等级的有符号和无符号整数之间的二进制运算中,有符号整数被转换为无符号整数。 由此引起的SO有很多问题……让我们称它为奇怪的行为: 无符号转换为签名转换 , C ++隐式转换(有符号+无符号) , 警告 – 有符号和无符号整数表达式之间的比较 , %(mod)与混合符号等
但是这些都没有给出任何理由,为什么标准采用这种方式,而不是倾向于签署的注册。 我确实找到了一位自封的大师,他说这是明显正确的做法,但他也没有给出任何理由: http : //embeddedgurus.com/stack-overflow/2009/08/a-tutorial-on- signed-and-unsigned-integers / 。
查看我自己的代码,无论我将有符号和无符号整数组合在一起,我总是需要从unsigned转换为signed。 有些地方没关系,但我没有找到一个代码示例,将有符号整数转换为unsigned是有意义的。
什么情况下铸造到未签名的正确的事情呢? 为什么标准是这样的?
如果无法表示值,则从无符号转换为签名会导致实现定义的行为。 从有符号到无符号的转换总是以无符号的bitsize的幂为模2,因此它总是被很好地定义。
如果在签名类型中可以表示每个可能的无符号值,则标准转换为签名类型。 否则,选择无符号类型。 这保证了转换始终是明确定义的。
笔记
-
如注释所示,C ++的转换算法是从Cinheritance的,以保持兼容性,这在技术上是C ++的原因。
-
有人建议,标准中定义签名到无符号转换而不是无符号签名转换的决定在某种程度上是任意的,而另一个可能的决定是对称的。 但是,可能的转换不是对称的。
在标准所考虑的两个非二进制补码表示中, n位有符号表示可以仅表示2 n -1个值,而n位无符号表示可以表示2 n个值。 因此,有符号无符号转换是无损的,可以反转(尽管永远不会产生一个无符号值)。 另一方面,无符号到符号的转换必须将两个不同的无符号值折叠到相同的有符号结果上。
在评论中,公式
sint = uint > sint_max ? uint - uint_max : uint
sint = uint > sint_max ? uint - uint_max : uint
提出了sint = uint > sint_max ? uint - uint_max : uint
。 这合并了值uint_max
和0; 两者都映射到0.即使对于非2s补码表示,这也有点奇怪,但对于2的补码,它是不必要的,更糟糕的是,它需要编译器发出代码以费力地计算这种不必要的混淆。 相比之下,标准的有符号无符号转换是无损的,在通常情况下(2的补码架构),它是无操作的。
这是一个半答案,因为我不太了解委员会的推理。
来自C90委员会的理由文件: https : //www.lysator.liu.se/c/rat/c2.html#3-2-1-1
自K&R出版以来,C在整体推广规则演变中的实施发生了严重分歧。 实施属于两个主要阵营,其特征可以是无符号保留和保值 。 这些方法之间的区别在于对
unsigned char
和unsigned short
,当由整体促销扩大时,但决定也会对常量的类型产生影响(见§3.1.3.2)。
……显然也是为了匹配任何运算符的两个操作数而进行的转换。 它继续:
在绝大多数情况下,这两种方案都给出了相同的答案,并且在具有二进制补码算法的实现中以及在有符号溢出上的安静环绕中,两者都给出了相同的有效结果 – 即,在大多数当前实现中。
然后,它指定了解释模糊的情况,并指出:
必须对结果进行有问题的签名 ,因为可以对签名或未签名的解释进行陈述。 只要
unsigned int
在运算符中面对有signed int
,并且signed int
具有负值,就会出现完全相同的歧义。 (在解决这种对抗的模糊性时,这两种方案都没有做得更好,或者更糟糕。)突然之间,负面signed int
成为一个非常大的unsigned int
,这可能是令人惊讶的 – 或者它可能正是所期望的知识渊博的程序员。 当然, 通过明智地使用演员阵容可以避免所有这些含糊之处。
和:
无符号保留规则极大地增加了
unsigned int
面对signed int
以产生可疑签名结果的情况的数量,而值保留规则最小化这种对抗。 因此,对于新手或不警惕的程序员来说,保值规则被认为更安全。 经过多次讨论,委员会决定支持保值规则,尽管UNIX C编译器已朝着无符号保留的方向发展。
因此,他们考虑int + unsigned
一个不需要的情况,并选择char
和short
转换规则尽可能少地产生这些情况, 即使当时大多数编译器遵循不同的方法。 如果我理解正确,那么这个选择迫使他们遵循当前选择的int + unsigned
产生一个unsigned
运算。
我仍然觉得这一切真的很奇怪。