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

peo*_*les 26 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
}
Run Code Online (Sandbox Code Playgroud)

这个运行得很好,直到我们达到了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;
}
Run Code Online (Sandbox Code Playgroud)

结果是:

numberA = 65536
numberB = 0
numberC = 0
numberD = 0
Run Code Online (Sandbox Code Playgroud)

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

Pas*_*uoq 19

所以似乎结果numberA + 1被提升为uint32_t

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

事实上,如果int是你的编译平台上的32位宽(意思是表示类型uint16_t具有较低的"转化率排名"比int),然后numberA + 1被计算为int的加成1和推广numberA为一体的整数提升规则,6.3.1.1部分: C11标准中的2:

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

[...]

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

在您的情况下,unsigned short很可能uint16_t是在您的平台上定义的内容,其所有值都可以表示为元素int,因此在算术运算中将unsigned shortnumberA提升为值int.


M.M*_*M.M 9

对于算术运算符,例如+,应用通常的算术转换.

为整数,这些转化的第一步骤被称为整数促销,这促进了类型小于的任何值int是一个int.

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

在表达式中numberA + 1,应用整数提升.1已经是一个int所以它保持不变.numberA具有uint16_tint您的系统更窄的类型,因此numberA被提升为int.

增加了两个结果ints是另一种int,并65535 + 1给出了65536因为你有32位int秒.

所以你的第一个printf输出结果.

在线:

numberB = numberA + 1;
Run Code Online (Sandbox Code Playgroud)

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

numberB = 65536;
Run Code Online (Sandbox Code Playgroud)

由于numberB具有无符号类型,uint16_t特别65536是减少(mod 65536)给出0.

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