为了定义转换规则,C99允许每个整数类型具有"整数转换等级"。下面按从最高级到最低级的顺序排列。
(1)long long int、usigned long long int
(2)long int、unsigned long int
(3) int 、unsigned int
(4) short int 、unsigned short int
(5) char、signed char、unsigned char
(6)_Bool
比较转换规则如下:
如果两个操作数类型相同,过程结束,否则依次尝试下面的规则:
(1):如果两个操作数都是有符号型或者都是无符号型,将整数转换等级较低的操作数转换为等级较高的操作数类型;
(2):如果无符号操作数的等级高于或等于有符号操作数的等级,将有符号操作数转换为无符号操作数的类型。比如比较unsigned int 与int,将int提升为unsigned int.
(3):如果有符号操作数类型可以表示无符号操作数类型的所有值,将无符号操作数转换为有符号操作数的类型。比如比较int 与unsigned short int,将unsigned short int提升为int
(4):将两个操作数都转换为与有符号操作数的类型相对应的无符号类型
stackoverflow上针对这一转换规则的一个回答stackoverflow
.
几个容易犯错的地方:

#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int a = -1;
    unsigned int b = 1;
 
    if(a > b)
        printf("a > b, a = %d, b = %u\n", a, b);
    else
        printf("a <= b, a = %d, b = %u\n", a, b);
    exit(0);
}

按照我们上面的规则比较unsigned int 与int 时将int 提升为unsigned int ,由于-1的底层表示为全一,因此转换为unsigned int时将为UINT_MAX即4294967295.因此a>b.
编译时添加选项-Wsign-compare可以检测出该问题。

[root@centos-linux-7 workspace]# gcc -Wsign-compare  -g -o compare compare.c
compare.c: In function main:
compare.c:9:10: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
     if(a > b)

在csapp书中第二章有几个经常犯错的例子:

float sum_element(float a[],unsigned length)
{
    int i;
    float result = 0;
    for(i=0;i<=length-1;i++)
        result+=a[i];

    return result;
}

错误分析,如果length参数传入0,0-1为-1,length为unsigned类型因此转为UINT_MAX,测试程序:

#include <stdio.h>
#include <limits.h>

int main()
{
    unsigned int i = -1;
    if(i == UINT_MAX)  //UINT_MAX:4294967295U
    {
        printf("Hello,world.\n");
    }
    return 0;
}

编译执行该程序,将输出Hello,world.

正确形式:

float sum_element(float arr[], unsigned length)
{
  int i;
  float result;

  for (i=0; i<=length-1; i++) {
    result += a[i];
  };
  return result;
}

第二个例子:

size_t strlen(const char*s);
int strlonger(char *s,char *t)
{
    return strlen(s)-strlen(t)>0;
}

上面的例子中判断字符串长度,返回的结果非零,原因和第一个例子相同,当s长度小于t时由于size_t 在stdio.h中可以看到是unsigned int因此会转为非负值。

csdn中有针对这几个例子的解释都是类型惹的祸——小心unsigned