C语言之整型提升

首先来看2个例子:

#include <stdio.h>

int main()
{
    unsigned char a = 0xff;
    char b = 0xff;
    int c = a==b;
    printf("C: %d\n", c);

    return 0;
}

你可以猜一下结果会打印多少呢?你可能以为打印时1,其实打印的是0!

#include <stdio.h>
#include <stdlib.h>
int main()
{
    unsigned char a, b;
    a = 0x0f;
    b = (~a) >> 4;
    printf("0x%x", b);
    system("pause");
    return 0;
}

按照正常逻辑,应该输出是0xf0,但实际输出是0xff。
为什么会这样呢?原因就在于整型提升。

什么是整型提升?

整型提升是C程序设计语言中的一项规定:在表达式计算时,各种整型如char、unsigned char、 short、unsigned short或者枚举等首先要提升为int类型,如果int类型不足以表示则要提升为unsigned int类型;然后执行表达式的运算。

signed char: -127 -> 127
unsigned char: 0 -> 255
signed short: -32767 -> 32767
unsigned short: 0 -> 65535
signed int: -2147483647 -> 2147483647

这一规则是由C语言的发明人丹尼斯·里奇(Dennis Ritchie)与肯·汤普逊(Ken Thompson)创设的 :

A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer maybe used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion.

整型提升的意义在于:表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算[1]。

C语言没有指定char类型的变量是无符号变量还是带符号变量。对于不同的机器,可能有不同的解释。在某些机器中,如果char类型值的最左一位为1,则转换为负整数(进行符号扩展),而在另一些机器中,把char类型值转换为int时,在char类型值的左边添加0,这样导致的转换结果值总是正值。

#include <stdio.h>
#include <stdlib.h>
int main()
{
    char a = 0xb6;                     //10110110
    short b = 0xb600;                   //10110110 00000000
    int c = 0xb6000000;                 //10110110 00000000 00000000 00000000
    if (a == 0xb6) printf("a\n");
    if (b == 0xb600) printf("b\n");
    if (c == 0xb6000000) printf("c\n");

    printf("%x,%x,%x\n",a,b,c);//输出:ffffffb6, ffffb600, b6000000
    system("pause");
    return 0;
}

这里的规则是:
若是有符号数,则前面补符号位。若是无符号数,则前面补0。

Visual C++或gcc编译下,上述程序输出为 c 。这是因为在这些环境下,编译器把char定义为signed char;表达式a==0xb6被整型提升,其中char类型的a提升为int类型并为一个负值,因此这个表达式的结果为false;表达式b==0xb600被整型提升,其中short类型的b提升为int类型并为一个负值,因此这个表达式的结果为false;表达式c == 0xb6000000没有做整型提升,==运算符的两段都是int类型的负值,其结果为true[2]。

这里要注意的是整型提升是由运算符引起的(见K&R C2rd P173),如:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    char c;
    char a = 2;
    char b = 0;
    c = a + b;
    printf("the size of the result of a+b: %lu\n", sizeof(a + b));
    printf("the size of the result of c: %lu\n", sizeof(c));
    system("pause");
    return 0;
}

输出为[3]:

the size of the result of a + b: 4
the size of the result of c : 1

由于a+b是一个运算表达式,且都为char型,符合整型提升的条件,所以需对a和b进行整型提升,然后执行“+”运算,计算结果(int型)再赋值给c(char型),又执行了隐式的类型转换。由于int型可以表示char的整个值域 -128-127,所以提升为int型,结果是4byte,但是 c没有运算符,不符合整型提升的条件不会进行提升还是1byte.

算术类型转换

许多运算符都会在运算过程中引起转换,并产生结果类型,其效果是将所有操作数转换为同一公共类型,并以此作为结果的类型,这种方式的转换成为普通算术类型转换。
根据K&R中描述的具体转换规则:
首先,如果参与运算的任何一个操作数为long double类型,则将另一个操作数转换为long double类型。
否则,如果,任何一个操作数为double类型,则将另一个操作数转换为 double类型。
否则,如果,任何一个操作数为float类型,则将另一个操作数转换为 float类型。
否则,同时对两个操作数进行整型提升,
然后,如果,任何一个操作数为unsigned long int 类型,则将另一个操作数转换为 unsigned long int 类型。
否则,如果,一个操作数long int 另一个为unsigned int 类型则结果依赖于long int类型能否表示所有的unsigned int ; 如果可以则将unsigned int类型转换long int(64位系统下long是8byte 可以表示unsigned int 但是32位环境不可以表示)。 如果不可以,则将两个数都转为unsigned long int
否则,如果,任何一个操作数为long int 类型,则将另一个操作数转换为 long int 类型。
否则,如果,任何一个操作数为unsigned int 类型,则将另一个操作数转换为 unsigned int 类型。
否则,将两个数都转换为int型。

总结:普通算术类型转换只在在操作数之间类型不一致的情况下发生,整型提升在操作数具有相同的类型时,仍有可能发生整型提升,比如两个char型运算[3]。

[1]Brian W. Kernighan and Dennis M. Ritchie. The C programming Language. Prentice-Hall. 1988. ISBN 0-13-110362-8
[2] 百度百科:整型提升
[3] https://www.cnblogs.com/jianghg/p/4417190.html

猜你喜欢

转载自blog.csdn.net/u010132497/article/details/81104319