为什么对无符号字符进行算术运算会将它们提升为有符号整数



许多类似问题的答案都指出,这是标准造成的。但是,我无法理解标准制定者做出这一决定的原因。

根据我的理解,unsigned char不会以2的补码形式存储值。因此,我不认为对两个unsigned chars进行XOR会产生意外行为。因此,将它们升级到int似乎只是浪费空间(在大多数情况下)和CPU周期。

此外,为什么是int?如果一个变量被声明为unsigned,那么无符号性对程序员来说显然很重要,因此在我看来,升级为unsigned int仍然比int更有意义。

[EDIT#1]如评论中所述,如果int不能充分容纳unsigned char中的值,则将升级为unsigned int

[编辑#2]为了澄清这个问题,如果它是关于在int上运行比在char上运行的性能优势,那么为什么它在标准中?这本可以作为一个建议给编译器设计者,以便更好地进行优化。现在,如果有人设计了一个不这样做的编译器,这将使他们的编译器成为一个不完全遵守C/C++标准的编译器,尽管假设这个编译器确实支持该语言的所有其他必需功能。简言之,我不知道为什么我不能直接在unsigned chars上操作,因此要求将它们升级到ints似乎没有必要。你能给我举个例子证明这是错误的吗?

您可以在线找到本文档:《国际标准原理-程序设计语言-C》(2003年5月10日修订版)。

第6.3章(第44-45页)是关于的转换

在K&R和C89的开发,在整数提升规则的演变过程中,实现之间出现了严重的分歧。实现分为两大阵营,其特征可以是无符号保留

值保留当整数提升扩大时,这些方法之间的差异集中在unsigned charunsigned short的处理上,但该决定也对常数的类型产生了影响(见§6.4.4.1)

无符号保留方法要求将两个较小的无符号类型提升为unsigned int。这是一个简单的规则,并产生一个独立于执行环境的类型。

值保留方法要求将那些类型提升为signed int,如果该类型可以正确地表示原始类型的所有值,则要求将这些类型提升为unsigned int

因此,如果执行环境将short表示为小于int的东西,则unsigned short变为int;否则变为CCD_ 24。在绝大多数情况下,这两种方案都给出了相同的答案,在使用二的补码算法和对有符号溢出的安静环绕的实现中,也就是在大多数当前实现中,这两个方案在更多情况下都给出了同样的有效结果。在这样的实现中,只有当这两个条件都成立时,两者之间才会出现差异:

  1. 涉及unsigned charunsigned short的表达式会产生一个int范围的结果,其中设置了符号位,也就是说,对这种类型进行一元运算,或者进行二进制运算,其中另一个操作数是int或"较窄"类型。

  2. 上一个表达式的结果用于其签名性显著的上下文中:

    sizeof(int) < sizeof(long),并且必须将其扩展为长类型或

    •在将右移定义为算术或的实现中,它是右移运算符的左操作数

    •它是/、%、<、<=,>,或>=。

在这种情况下,会产生真正的解释歧义。结果必须被称为有疑问的签名,因为可以为签名或无签名的解释设置案例。每当unsigned int通过运算符与signed int对抗,并且signed int具有负值时,就会出现完全相同的模糊性。在解决这场对抗的模糊性方面,这两种方案都没有起到更好或更坏的作用。突然间,负signed int变成了非常大的unsigned int,这可能令人惊讶,也可能正是知识渊博的程序员所希望的。当然,所有这些歧义都可以通过明智地使用强制类型转换来避免

探索这个问题的一个重要结果是理解高质量的编译器可能会很好地寻找这种有问题的代码并提供(可选的)诊断,而认真的导师可能会很容易地警告程序员隐式类型转换的问题。

无符号保留规则极大地增加了unsigned intsigned int对抗以产生有疑问的有符号结果的情况的数量,而值保留规则最小化了这种对抗。因此,对于新手或粗心的程序员来说,保值规则被认为是更安全的。经过多次讨论,C89委员会决定支持值保留规则,尽管UNIXC编译器已经朝着无符号保留的方向发展。

C89 的安静变化

一个依赖于无符号保留算术转换的程序会有不同的行为,可能不会引起抱怨。这被认为是C89委员会对目前广泛使用的做法所做的最严重的语义改变。

作为参考,您可以在Lundin的回答中找到有关更新到C11的转换的更多详细信息。

最新更新