为什么在C中添加时会提升整数类型?

时间:2015-04-23 08:34:58

标签: c integer-promotion

所以我们遇到了一个字段问题,经过几天的调试后,将问题缩小到这个特定的代码位,其中while循环中的处理没有发生:

// heavily redacted code
// numberA and numberB are both of uint16_t
// Important stuff happens in that while loop

while ( numberA + 1 == numberB )
{
    // some processing
}

这个运行正常,直到我们达到了65535的uint16限制。稍后的另一堆打印语句,我们发现numberA + 1的值为65536,而numberB包裹了到0。检查失败,没有进行任何处理。

这让我很好奇,所以我整理了一个快速的C程序(用GCC 4.9.2编译)来检查:

#include <stdio.h>
#include <stdint.h>

int main()
{

    uint16_t numberA, numberB;
    numberA = 65535;
    numberB = numberA + 1;

    uint32_t numberC, numberD;
    numberC = 4294967295;
    numberD = numberC + 1;

    printf("numberA = %d\n", numberA + 1);
    printf("numberB = %d\n", numberB);

    printf("numberC = %d\n", numberC + 1);
    printf("numberD = %d\n", numberD);

    return 0;
}

结果是:

numberA = 65536
numberB = 0
numberC = 0
numberD = 0

所以似乎numberA + 1的结果被提升为uint32_t。这是C语言的意图吗?或者这是一些编译器/硬件奇怪吗?

4 个答案:

答案 0 :(得分:19)

  

因此numberA + 1的结果似乎已提升为uint32_t

添加的操作数在添加发生之前被提升为int,并且添加的结果与有效操作数(int的类型相同)。

的确,如果int在您的编译平台上为32位宽(意味着代表uint16_t的类型的“转换排名”低于int),那么{{1计算为numberA + 1和升级int之间的1加法,作为整数提升规则的一部分,在C11标准中为6.3.1.1:2:

  

可以在任何可以使用int或unsigned int的表达式中使用以下内容:[...]具有整数类型(int或unsigned int除外)的对象或表达式,其整数转换等级小于或等于int和unsigned int的等级。

     

[...]

     

如果int可以表示原始类型的所有值[...],则该值将转换为int

在您的情况下,numberA很可能在您的平台上定义了unsigned short,其所有值都可以表示为uint16_t的元素,因此{{1}当值int在算术运算中出现时,它会被提升为unsigned short

答案 1 :(得分:9)

对于+等算术运算符,将应用通常的算术转换

对于整数,这些转换的第一步称为整数促销,这会将小于int的任何类型的值提升为int

其他步骤不适用于您的示例,因此我将省略它们以简洁。

在表达式numberA + 1中,应用整数提升。 1已经是int,因此保持不变。 numberA的类型uint16_t比系统上的int窄,因此numberA会升级为int

添加两个int的结果是另一个int65535 + 1给出65536,因为您有32位int

因此,您的第一个printf会输出此结果。

在行中:

numberB = numberA + 1;

上述逻辑仍适用于+运算符,这相当于:

numberB = 65536;

由于numberB具有无符号类型,具体为uint16_t,因此65536会减少(mod 65536),从而产生0

请注意,最后两个printf语句会导致未定义的行为;您必须使用%u打印unsigned int。要处理不同大小的int,您可以使用"%" PRIu32获取uint32_t的格式说明符。

答案 2 :(得分:4)

当开发C语言时,最好尽量减少算术编译器必须处理的种类。因此,大多数数学运算符(例如加法)仅支持int + int,long + long和double + double。虽然可以通过省略int + int来简化语言(将所有内容提升为long),但对long值的算术通常需要2-4倍于int值的算术;由于大多数程序都以int类型的算术为主,因此成本非常高。相反,将float提升为double会在很多情况下保存代码,因为这意味着只需要两个函数来支持float:转换为double,然后转换来自double。所有其他浮点算术运算只需要支持一个浮点类型,并且由于浮点数学通常通过调用库例程来完成,因此调用例程来添加两个double值的成本通常与调用例程来增加两个float值的成本。

不幸的是,在任何人真正弄清楚0xFFFF + 1应该是什么意思之前,C语言在各种平台上变得普遍,到那时已经有一些编译器表达式产生了65536而一些产生了零。因此,标准编写者已经努力以一种允许编译器继续做他们正在做的事情的方式编写它们,但是从任何希望编写可移植代码的人的角度来看,它都是无益的。因此,在int为32位的平台上,0xFFFF + 1将产生65536,而在int为16位的平台上,它将产生零。如果在某个平台上int碰巧是17位,那么0xFFFF + 1会授权编译器否定时间和因果律[btw,我不知道是否有任何17位平台,但有一些32其中uint16_t x=0xFFFF; uint16_t y=x*x;会导致编译器混淆先于它的代码行为的位平台。

答案 3 :(得分:0)

THREE.BufferGeometry中的文字1,即在你的情况下为int32类型,因此使用int32和int16的操作给出了int32的结果。

要将int语句的结果设为numberA + 1,请为uint16_t尝试显式类型广播,例如:1