无符号和较大签名类型之间隐式转换的不一致行为

Grz*_*ski 13 c c99 c89 integer-promotion implicit-conversion

考虑以下示例:

#include <stdio.h>

int main(void)
{
    unsigned char a  = 15; /* one byte */
    unsigned short b = 15; /* two bytes */
    unsigned int c   = 15; /* four bytes */

    long x = -a; /* eight bytes */
    printf("%ld\n", x);

    x = -b;
    printf("%ld\n", x);

    x = -c;
    printf("%ld\n", x);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译我正在使用GCC 4.4.7(它没有给我任何警告):

gcc -g -std=c99 -pedantic-errors -Wall -W check.c
Run Code Online (Sandbox Code Playgroud)

我的结果是:

-15
-15
4294967281
Run Code Online (Sandbox Code Playgroud)

问题是为什么两者unsigned charunsigned short值都正确地"传播"到(签名)long,而unsigned int不是?对此有任何参考或规则吗?

以下是gdb(单词以小端顺序排列)的结果:

(gdb) x/2w &x
0x7fffffffe168: 11111111111111111111111111110001    11111111111111111111111111111111 

(gdb) x/2w &x
0x7fffffffe168: 11111111111111111111111111110001    00000000000000000000000000000000
Run Code Online (Sandbox Code Playgroud)

Sha*_*our 11

这是由于整数提升如何应用于操作数以及一元减号的结果具有相同类型的要求.这在一6.5.3.3 元算术运算符一节中有所说明(强调我的未来):

一元运算符的结果是其(提升的)操作数的否定.整数提升在操作数上执行,结果具有提升类型.

和c99标准部分6.3 转换中涵盖的整数提升转换并说:

如果int可以表示原始类型的所有值,则该值将转换为int; 否则,它将转换为unsigned int.这些被称为整数促销.48)所有其他类型的整数提升不变.

在前两种情况下,促销将是int,结果将是int.在unsigned int的情况下,不需要升级,但结果将需要转换回unsigned int.

使用符号和无符号整数部分中规定的规则将其-15转换为unsigned int,其中说明:6.3.1.3

否则,如果新类型是无符号的,则通过重复地添加或减去一个可以在新类型中表示的最大值来转换该值,直到该值在新类型的范围内.49)

因此,我们最终会得到-15 + (UMAX + 1)哪些结果UMAX - 14会产生大的无符号值.这有时为什么你会看到代码使用-1转换为无符号值以获得类型的最大无符号值,因为它总是最终成为-1 + UMAX + 1哪个UMAX.