目录
前言
在C语言中,往往会操作不同类型的数据,此时,需要考虑类型转换。对此,C语言提供了两种不同类型的转换方式:隐式类型转换和显示类型转换(也称强制类型转换)。其中,隐式类型转换由编译器自行处理。
十一、隐式类型转换
隐式类型转换分为整型提升和算数转换。
11.1 整型提升
C语言的整型算术运算总是至少以缺省整型类型的精度来进行的。为获取这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换成为整型提升。
那么,如何整型提升呢?
整型提升的前提:只有当表达式中出现长度可能小于int型的整型值时,才需要对该值进行整型提升,转换为int或unsigned int,然后执行表达式的运算。
char是signed char还是unsigned char,int是signed int还是unsigned int,等等,在C语言中并没有明确规定,而是取决于编译器。
整型提升的规则:按照变量数据类型的符号位来提升的。
- 无符号数,高位补0;
- 有符号数,高位补符号位。
1.正数的整形提升:
char a = 1;
//0000 0000 0000 0000 0000 0000 0000 0001
//char类型只存放8个bit位
//所以,存放的是0000 0001,正数,原码就是补码
//所以,按符号位提升,8位最高位是0,所以,整型提升时,高位补0
//提升后:0000 0000 0000 0000 0000 0000 0000 0001
2.负数的整形提升:
char a = -1;
//1111 1111 1111 1111 1111 1111 1111 1111
//char类型只存放8个bit位
//所以,存放的是1111 1111,负数,存放的是补码
//所以,按符号位提升,8位最高位是1,所以,整型提升时,高位补1
//提升后的补码:1111 1111 1111 1111 1111 1111 1111 1111
//提升后的原码:1000 0000 0000 0000 0000 0000 0000 0001
通过对整型提升的认识,我们很容易理解下面代码:
#include<stdio.h>
int main()
{
char a = 5;
//0000 0000 0000 0000 0000 0000 0000 0101,但char类型只能存放8个bit位,即0000 0101
//存入的是:0000 0101 正数,原码就是补码
char b = 126;
//同理: 0111 1110
char c = a + b;//看到相加,char类型,都达不到整型大小,因此使用之前需要整型提升
//0000 0101 signed char 按照符号位提升,8位看最高位是符号位0,因此,整型提升的时候,高位都补0
//0000 0000 0000 0000 0000 0000 0000 0101
//0111 1110
//0000 0000 0000 0000 0000 0000 0111 1110
//相加
//0000 0000 0000 0000 0000 0000 1000 0011
//char c中能放的是: 1000 0011
printf("%d\n", c);//-125 //打印整型,又要以符号位进行整型提升
//1111 1111 1111 1111 1111 1111 1000 0011 补码
//1000 0000 0000 0000 0000 0000 0111 1100 取反
//1000 0000 0000 0000 0000 0000 0111 1101 原码
return 0;
}
此外,我们通过sizeof操作符,对整型提升进行验证:
#include<stdio.h>
int main()
{
char c = 1;
printf("%u\n", sizeof(c));//1 //sizeof(c),c没有参与运算,故就是求char类型大小
printf("%u\n", sizeof(+c));//4 整型提升
printf("%u\n", sizeof(-c));//4 整型提升
//sizeof(+c),sizeof(-c),c参与运算,整型提升为int,故就是求int类型大小
return 0;
}
11.2 算数转换
算数转换也属于隐式转换的一种,我们刚刚讨论的是类型小于整形的情况,算术转换的都是大于等于int类型的数据。如果某个操作符的各个操作数属于不同的类型,那么计算是无法进行下去的,除非将这些操作数全都转化为同一类型。因此,编译器是按照下图中由下向上转换的逻辑将不同的数据类型转换为相同。
- long double
- double
- float
- unsigned long int
- long int
- unsigned int
- int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。例如:
#include<stdio.h>
int main()
{
int i = -1;
if(i < sizeof(i))
{
printf("1\n");
}
else
{
printf("2\n");
}
return 0;
}
代码执行结果为:
单看这个代码,sizeof(-1)的计算结果是4,-1<4应该输出1,但是结果却得到2。
究其原因,我们之前提到过,sizeof的返回值为size_t类型,在C中被定义为无符号整型类型,即unsigned int。所以,按照算数转换的规则,int i要转换为unsigned int。转换之后是一个非常大的数字,因此程序执行打印2。
12.3 操作符的属性
C语⾔的操作符具有优先级和结合性两个属性,这两个属性决定了表达式的计算顺序,在此过程中,我们还需要考虑是否控制求值顺序。我们通过代码进行理解:
#include<stdio.h>
int main()
{
//优先级:相邻操作符,优先级高的先计算
int a = 3 + 5 * 4;
//结合性:相邻操作符优先级相同的情况下,结合性才有作用
int b = 3 + 4 + 5;//从左向右结合
return 0;
}
值得注意的是,下面代码的写法是有问题的:
int main()
{
a* b + c * d + e * f;//当a b c d e f分别是表达式时,计算将会出现问题,因此,表达式应该拆分写
return 0;
}
C语言操作符优先级参照:https://zh.cppreference.com/w/c/language/operator_precedence