前言
今天小羊又来给铁汁们分享关于C语言的隐式类型转换规则
,在C语言中类型转换方式可分为隐式类型转换
和显式类型转换
(强制类型转换),其中隐式类型转换是由编译器自动进行,无需程序员干预,今天小羊课堂说的就是关于隐式类型转换,隐式类型转换分为两种情况:整型提升和算术转换
。
一、隐式类型转换的规则
在c语言中,自动类型转换遵循以下规则:
- 若参与运算量的类型不同,则先转换成同一类型,然后进行运算。
- 转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。
a、若两种类型的字节数不同,转换成字节数高的类型
b、若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型 - 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。
- char型和short型参与运算时,必须先转换成int型。
- 在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入。
二、整型提升
C的整型算术运算总是至少以缺省整型类型的精度来进行的,为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
原理
有符号补符号位,无符号位无脑补0
1.负数的整型提升
高位补充符号位,即补1
char a=-1;
变量a的二进制位(补码)中只有8个比特位:
11111111
因为char是有符号的char
所以整型提升的时候,补符号位,即补1
提升结果:
11111111 11111111 11111111 11111111
2.正数的整型提升
高位补充符号位,即补0
char a=1;
变量a的二进制位(补码)中只有8个比特位:
00000001
因为char是无符号的char
所以整型提升的时候,补符号位,即补0
提升结果:
00000000 00000000 00000000 00000001
3.无符号的整型提升
无符号整型提升,高位补0(无符号只有正数)
三、整型提升实例
例1:
#include <stdio.h>
int main()
{
char a = 5, b = 127;
char c = a + b;
int d = a + b;
printf("c=%d\n", c);
printf("d= %d", d);
return 0;
}
运行结果:
c=-124
d=132
分析:
char a=5
0000 0101 --> a=5
char b=127
0111 1111 --> b=127
因为参与了运算,并且char类型的精度小于int类型,所以这里进行整型提升:
00000000 00000000 00000000 00000101 --> a=5
00000000 00000000 00000000 01111111 --> b=127
00000000 00000000 00000000 10000100 --> c=132
//1:>
将结果存入类型为char的变量c中,c只能存储8位,所以保留结果最后8位
1000 0100 --> c=132
由于char类型也是有正负的,且计算结果是以补码形式,转化为原码
补码:1000 0100
反码:1000 0011
原码:1111 1100 --> -124
原码值为-124
//2:>
运算还是先整型提升再运算,二进制同上,最后结果存放到int类型的b中,所以直接就是132
例2:
#include<stdio.h>
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)
printf("c");
return 0;
}
运行结果:
c
分析:
a=0xb6
整型提升前:10110110
整型提升后:11111111 11111111 11111111 10110110 可以直接看出这是一个负数的补码
b=0xb600
整型提升前:10110110 00000000
整型提升后:11111111 11111111 10110110 00000000 可以直接看出这也是一个负数的补码
c=0xb6000000
无需整型提升,故结果为真
例3:
#include<stdio.h>
int main()
{
char c = 1;
printf("c=%u\n", sizeof(c));//%u按无符号整形unsigned int打印
printf("c=%u\n", sizeof(+c));
printf("c=%u\n", sizeof(-c));
return 0;
}
运行结果:
c=1
c=4
c=4
分析:
sizeof(c),c没有参与运算,故就是求char类型大小
sizeof(+c),sizeof(-c),c参与运算,整型提升为int,故就是求int类型大小
四、算术转移
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行,下面的寻常算术转换。
//从高到低
long double
double
float
unsigned long int
long int
unsigned int
int
注:
如果在同一运算中操作数类型不同,等级低的要往等级高的转换。
算术转换要合理,否则会存在潜在的问题
例1:
float f=3.14;
int num=f;//隐式转换,精度丢失
例2:
#include<stdio.h>
int main()
{
unsigned char a = 0;
unsigned char b = 255;
unsigned char c = 255;
a = b + c;
printf("a = %d\n", a);
return 0;
}
运行结果:
a = 254
分析:
b和c的值都需要提升为整型,再执行加法运算
b\c:>
整型提升前:11111111
整型提升后:11111111 11111111 11111111 11111111
11111111 11111111 11111111 11111111 --> b
11111111 11111111 11111111 11111111 --> c
111111111 11111111 11111111 11111110 --> a
结果保留最后的8位
11111110 --> 补码
由于是无符号char类型,那么原反补一样
原码也是11111110 即为254
总结
发生转换的原因:
硬件:CPU寄存器的比特位是统一的,将内存中的数据放入寄存器中就会发生隐式转换
软件:C语言的操作符对多个操作数进行操作时,必须保证其类型一致
五、操作符的属性
1.操作符
复杂表达式的求值有三个影响的因素。
操作符的优先级
。决定了有多个操作符和多个操作数时,先执行哪部分。操作符的结合性
。当优先级相同,多个或单个操作符之间从左向右执行还是从右向左执行。是否控制求值顺序
。特定的某些表达式在进行求值,根据不同的条件产出不同的求值过程。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
2.操作符优先级
操作符优先级,从上往下,重点的
操作符 | 结合性 | 是否控制求值顺序 |
---|---|---|
() | N/A | 否 |
, | L-R | 否 |
-> | L-R | 否 |
++ | L-R | 否 |
– | L-R | 否 |
++ | R-L | 否 |
– | R-L | 否 |
* | R-L | 否 |
3.问题表达式
表达式1
a*b + c*d + e*f
注释:代码1在计算的时候,由于乘法的优先级比+的优先级高,只能保证的乘法计算是比+早,但是优先级并不能决定第三个*比第一个+早执行。
所以表达式的计算机顺序就可能是:
a*b
c*d
a*b + c*d
e*f
a*b + c*d + e*f
或者:
a*b
c*d
e*f
a*b + c*d
a*b + c*d + e*f
表达式2
非法表达式1
int main()
{
int i = 10;
i = i-- - --i * (i = -3) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
非法表达式2
int fun()
{
static int count = 1;
return ++count; }
int main()
{
int answer;
answer = fun() - fun() * fun();
printf( "%d\n", answer);//输出多少?
return 0; }
这两个表达式,铁汁们要好好思考这为什么是非法的,不懂得可以私信小羊哦
总结:
我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式一定存在问题
好了,今天小羊分享的C语言的隐式类型转换规则就讲到这里了,欢迎大家评论区留言~~