C语言不同于pascal语言,在不同类型相互赋值时有一定可能进行自动类型转换。C语言中按照类型级别从大到小排序为
long double
double
float
unsigned long long
long long
unsigned long
long
unsigned int
int
当涉及多种不同类型的混合运算时,编译器会自动转换类型,变量都会升级至参与运算中所有数据类型的最高级别的类型。比如说char、int、double的类型在进行运算时都会升级到double类型。
举个例子:
若存在以下的变量
char a = 1;
int i = 2;
double d = 1.5;
在以下对变量s赋值的过程中
short s = a + i + d;
升级还是降级的过程中都不会导致变量的数据类型进行改变,而是赋值给一个const的临时变量,再使用这个临时变量进行运算。
首先d为这几个变量中的最高级的数据类型,a 和 i则升级至double类型,将值保存在一个临时变量中分别为1.0和2.0,所以右边的值为4.5,但是s为short类型,会发生数据截断,略去小数部分,则s 值为4。在这个过程中,a和i并不会改变其数据类型。
以上没有提到short和char类型是因为在表达式中,这两种数据类型都会提升至int。而在浮点型进行运算时都是以双精度进行运算的,即使是float类型也会提升至double类型进行运算
同样的在函数传参的过程中,char和short都会升级至int,float则会升级至double。
数据类型升级的过程中不会出现问题,编译器不会出现任何的告警。但是要注意升级过程中导致的结果和我们预想的不一样。
同样的在函数传参的过程中,char和short都会升级至int,float则会升级至double。
数据类型升级的过程中不会出现问题,编译器不会出现任何的告警。但是要注意升级过程中导致的结果和我们预想的不一样。
数据截断和数据失真
数据截断一般发生在不同的整型变量进行转换时,比如说,当将一个int类型的值赋给一个char类型变量,只会将低位的字节赋值给char,而高位的字节会被省略掉。
比如说以下赋值,
char ch = 257;
一般编译器都会默认整型常量为int类型,所以我们可以将257写为2进制的形式:
00000000 00000000 00000001 00000001
只取低位字节,则ch 的值应该为00000001,也就是1.
数据失真发生在整型和浮点类型进行转换时,当一个浮点型数据赋值给整型变量时,编译器会将小数部分直接略去,取整数部分给整型变量赋值,数据失真一般编译器都会给出告警,若要消除告警则需要使用强制转换运算符。
整型提升
前面提到过short和char类型都会自动提升至int类型,实际上比int所占字节小的类型,无论是unsigned还是signed的char和short在表达式中都会进行整形提升,全部提升至int类型
提升的规则为:
- signed 有符号类型 的二进制补码按照符号位(表示正负的位数)进行提升,负数向前补1,正数则向前补0;
- unsigned 无符号类型的二进制补码直接向前补0。
例子:
char a = 127;
char b = 3;
char c = a+b;
printf("%d %u\n", c, c);
在控制台上的输出结果为:
-126 4294967170
首先a+b先进行整型提升,分别提升
至
a: 00000000 00000000 00000000 01111111
b: 00000000 00000000 00000000 00000011
计算结果为
00000000 00000000 00000000 10000010
将结果赋值给c,则进行
数据截断,为
10000010
在传入printf后,进行整型提升为
11111111 11111111 11111111 0000010
在传入printf后,进行整型提升为
11111111 11111111 11111111 0000010
若视为%d,有符号类型,则实际值按照补码变为原码的规则则为-126,按照%u的规则则认为原码与补码相同,为4294967170
由此可见在这个过程中,整型提升和数据截断这两个机制都导致了结果和我们预想中的不一样
若不进行赋值,直接传入表达式到printf函数中,则不会数据截断
char a = 127;
char b = 3;
printf("%d %u\n", a+b, a+b);
结果为:130 130
因为没有赋值,所以计算结果不需要截断,而是在整型提升后直接将计算结果传入,按照整型打印
除了赋值运算,其他运算也会因为整型提升而导致结果和我们预想的不一样
可以观察以下的代码
int main()
{
char a = 0xbf;
short s = 0xbfff;
int i = 0xbfffffff;
if(a == 0xbf)
{
printf("a == 0xbf\n");
}
if(s == 0xbfff)
{
printf("b == 0xbfff\n");
}
if(i == 0xbfffffff)
{
printf("i == 0xbfffffff\n");
}
return 0;
}
控制台的打印结果只有
i == 0xbfffffff
这是因为在赋值的时候,整数常量都为int类型,而在进行比较时,a和b都会升级至int类型,按照整型提升的规则,由于a和b都为有符号类型且符号位为1,提升后分别为0xffffffbf和0xffffbfff,与相比较的整型常量不一致。
无符号整型和有符号整型的转换
若存在以下的赋值表达式:
unsigned char ch = -10;
可以将 -10 转换为补码
过程为
原码:00000000 00000000 00000000 10001010
反码:11111111 11111111 11111111 11110101
补码:11111111 11111111 11111111 11110110
将值赋值给ch 时出现数据截断 为
11110110
但是 ch 为无符号类型
原码反码补码一致,所以 ch 的值为246
强制类型转换
编译器在编译时会自动根据上下文环境进行自动类型转换,但是有些数据的转换是编译器无法自行判断的,这时就需要使用强制类型转换运算符进行类型转换
强转形式
(type_name) expression
如果存在以下三种变量
int i =1;
float f = 1.2;
double d = 2.2;
(1)若
i = f;
没有什么缺点,编译器会通过
(2)若
d = i;
则会出现数据失真,编译器一般会给出报警,若想要消除告警则需要使用强转。
(3)如果将char* 和 int *或者 int*和int进行相互赋值(不使用强转),则编译器会报错,就需要使用强制类型转换避免。但是这种转换若不是项目需要一般没有必要进行这种处理.