微信公众号:二进制人生
专注于嵌入式linux开发。问题或建议,请发邮件至hjhvictory@163.com。
更新:2020/05/21,转载请注明出处。
C语言进阶修炼之--隐式类型转换的陷阱
隐式类型转换是C语言的一大诡异之处,它造成的危害程度与数组和指针有的一拼。语句或表达式通常应该只使用一种类型的变量和常量。然而,如果你混合使用类型,C使用一个规则集合来自动完成类型转换。这可能很方便,但也很危险。
a) 当出现在表达式里时,有符号和无符号的char和short类型都将自动被转换为int类型,在需要的情况下,将自动被转换为unsigned int(在short和int具有相同大小时)。这称为类型提升。提升在算数运算中通常不会有什么大的坏处,但如果位运算符 ~ 和 << 应用在基本类型为unsigned char或unsigned short 的操作数,结果应该立即强制转换为unsigned char或者unsigned short类型(取决于操作时使用的类型)。
unsigned char port = 0x5aU; // 0101 1010
unsigned char result_8;
result_8 = (~port) >> 4; //1010 0101 >> 4
假如我们不了解表达式里的类型提升,认为在运算过程中变量port一直是unsigned char类型的。我们来看一下运算过程:~port结果为0xa5,0xa5>>4结果为0x0a,这是我们期望的值。
但实际上,result_8的结果却是0xfa!在ARM 32结构下,int类型为32位。变量port在运算前被提升为int类型:~port结果为0xff ff ff a5
,0xff ff ff a5>>4结果为0x0f ff ff fa
,赋值给变量result_8,发生类型截断(这也是隐式的!),result_8=0xfa。经过这么诡异的隐式转换,结果跟我们期望的值,已经大相径庭!正确的表达式语句应该为:
result_8 = (unsigned char) (~port) >> 4; /*强制转换*/
还有一个例子:
char a = 0x80;
if( a == 0x80)
printf("equal\n");
实际上不会打印equal,根据转化规则,a先转换成0xFFFFFF80,再和0x80做比较。
char提升为int,高位应该填0还是填1?
如果是unsigned的类型转换成int类型,高位补0.
如果是signed的类型转换成int类型,如果原来最高位是1则补1,如果是0则补0。
2) 如果int和unsigned int一起运算,会将int转为unsigned int,这种操作如果放在while和if语句的条件判断中,会有想不到的结果,所以要小心小心。
#include <stdio.h>
#include <string.h>
int main()
{
const char *str = "abcdef";
int i = -1;
if(strlen(str) > i){
printf("Yes\n");
}
else{
printf("No\n");
}
printf("-1的无符号类型:%u\n", -1);
return 0;
}
str的长度是6,必然大于-1,但运行结果是No,这说明,整型i在和size_t类型的数作比较时,-1变成了size_t类型,即有符号数变成了无符号数,是一个很大的数,远远大于6,所以输出No。
3) return 语句会将变量或者表达式的值转换成函数定义的返回值类型,这一点非常隐蔽。
unsigned int fun()
{
char a = 0x80;
return a;
}
unsigned int a = fun();
printf("a=%u\n",a);
char如果是个负数,转为unsigned int或者int,扩展位全部填1,即0x80变成0xFFFFFF80;如果不是负数,扩展位全部填0。
return a相当于return (unsigned int)a。
结论1
C语言中比int小的整型(包括short 、unsigned short 、 unsigned char和char)在运算中都要转换成int然后进行运算,至于为什么选择转换为int,应该是从效率上考虑的,因为通常情况下int的长度被定义为机器处理效率最高的长度,比如32位机上,一次处理4个字节效率是最高的,所以虽然short(我机器上占2个字节)更节省内存,但是在运算中的效率,是int更高。
但是还需要注意一个问题就是转换成int类型的时候,高位补齐的问题。
如果是unsigned的类型转换成int类型,高位补0.
如果是signed的类型转换成int类型,如果原来最高位是1则补1,如果是0则补0。
对于unsigned short和short进行运算,和char和unsigned char一样,都是要先转换成int,然后再进行运算。
转换时高位补齐的方法也和unsigned char、char一样。
unsigned char a = 0x02 ;
(int)a = 0x 00 00 00 02;
char a = 0x02; 0000 0010
(int)a = 0x00 00 00 02;
char a = 0x80; 1000 0000
(int)a = 0xFF FF FF 80;
结论2
unsigned int和int做运算会将int看成unsigned int,而且结果也是unsigned int。
结论3
return返回时如果类型不一致也会做类型转换。
每天进步一点点……