暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

隐式类型转换的陷阱

二进制人生 2020-05-22
485

微信公众号:二进制人生
专注于嵌入式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 = 000 00 00 02;

char a = 0x020000 0010 
(int)a = 0x00 00 00 02;

char a = 0x801000 0000
(int)a = 0xFF FF FF 80

结论2

unsigned int和int做运算会将int看成unsigned int,而且结果也是unsigned int。

结论3

return返回时如果类型不一致也会做类型转换。

每天进步一点点……

图 二进制人生公众号



文章转载自二进制人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论