为什么整数提升的结果不同?

lin*_*xer 15 c integer

请看我的测试代码:

#include <stdlib.h>
#include <stdio.h>


#define PRINT_COMPARE_RESULT(a, b) \
    if (a > b) { \
        printf( #a " > " #b "\n"); \
    } \
    else if (a < b) { \
        printf( #a " < " #b "\n"); \
    } \
    else { \
        printf( #a " = " #b "\n" ); \
    }

int main()
{
    signed   int a = -1;
    unsigned int b = 2;
    signed   short c = -1;
    unsigned short d = 2;

    PRINT_COMPARE_RESULT(a,b);
    PRINT_COMPARE_RESULT(c,d);

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

结果如下:

a > b
c < d
Run Code Online (Sandbox Code Playgroud)

我的平台是Linux,我的gcc版本是4.4.2.我对输出的第二行感到惊讶.第一行输出是由整数提升引起的.但为什么第二行的结果不同?

以下规则来自C99标准:

如果两个操作数具有相同的类型,则不需要进一步转换.否则,如果两个操作数都具有有符号整数类型或两者都具有无符号整数类型,则具有较小整数转换等级类型的操作数将转换为具有更高等级的操作数的类型.

否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则具有有符号整数类型的操作数将转换为具有无符号整数类型的操作数的类型.

否则,如果带有符号整数类型的操作数的类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数将转换为带有符号整数类型的操作数的类型.

否则,两个操作数都转换为无符号整数类型,对应于带有符号整数类型的操作数的类型.

我认为两个比较都应该属于同一个案例,第二个案例是整数提升.

Die*_*Epp 20

使用算术运算符时,操作数将进行两次转换.

整数提升:如果int可以表示该类型的所有值,则将操作数提升为int.这适用于大多数平台shortunsigned short大多数平台.在此阶段执行的转换分别在每个操作数上完成,而不考虑其他操作数.(有更多规则,但这是适用的规则.)

通常的算术转换:如果您将a unsigned int与a 进行比较signed int,因为既不包括另一个的整个范围,并且两者都具有相同的等级,则两者都会转换为该unsigned类型.在检查两个操作数的类型之后完成此转换.

显然,如果没有两个操作数,"通常的算术转换"并不总是适用.这就是为什么有两套规则.例如,一个问题是移位运算符<<并且>>不执行通常的算术转换,因为结果的类型应该仅取决于左操作数(因此,如果您看到某个类型x << 5U,则U表示"不必要").

细分:假设一个典型的系统具有32位int和16位short.

int a = -1;         // "signed" is implied
unsigned b = 2;     // "int" is implied
if (a < b)
    puts("a < b");  // not printed
else
    puts("a >= b"); // printed
Run Code Online (Sandbox Code Playgroud)
  1. 首先,两个操作数被提升.因为两者都是,int或者unsigned int没有促销.
  2. 接下来,将两个操作数转换为相同的类型.由于int不能代表所有可能的值unsigned,并且unsigned不能代表所有可能的值int,因此没有明显的选择.在这种情况下,两者都转换为unsigned.
  3. 从有符号转换为无符号时,会将2 32重复添加到有符号值,直到它在无符号值的范围内.就处理器而言,这实际上是一个noop.
  4. 所以比较变得if (4294967295u < 2u)虚假.

现在让我们尝试一下short:

short c = -1;          // "signed" is implied
unsigned short d = 2;
if (c < d)
    puts("c < d");     // printed
else
    puts("c >= d");    // not printed
Run Code Online (Sandbox Code Playgroud)
  1. 首先,两个操作数被提升.由于两者都可以忠实地表现出来int,所以两者都被提升为int.
  2. 接下来,它们将转换为相同的类型.但它们已经是同一类型int,所以什么都没做.
  3. 所以比较变成了if (-1 < 2)现实.

编写好的代码:有一种简单的方法可以在代码中捕获这些"陷阱".只需编译并打开警告,并修复警告.我倾向于编写这样的代码:

int x = ...;
unsigned y = ...;
if (x < 0 || (unsigned) x < y)
    ...;
Run Code Online (Sandbox Code Playgroud)

您必须注意,您编写的任何代码都不会遇到其他已签名和未签名的陷阱:签名溢出.例如,以下代码:

int x = ..., y = ...;
if (x + 100 < y + 100)
    ...;
unsigned a = ..., b = ...;
if (a + 100 < b + 100)
    ...;
Run Code Online (Sandbox Code Playgroud)

一些流行的编译器将优化(x + 100 < y + 100)(x < y),但这是另一天的故事.只是不要溢出你签名的号码.

附注:请注意,虽然signed是隐含的int,short,long,和long long,这是不是暗示了char.相反,它取决于平台.