在介绍C语言的整型提升概念之前,我们先来看一段实际的代码例子和仿真结果。
主要代码如下:
复制
intmain(){unsignedchari=0;unsignedchartemp=0;unsignedchardat=0x89;unsignedcharSER=0;printf("===========method 1=============\r\n");//方式1dat=0x89;for(i=0; i<8; i++){temp = dat<<i;SER = temp>>7;printf("%d\r\n", SER);}printf("===========method 2=============\r\n");//方式2dat=0x89;for(i=0; i<8; i++){SER = dat >>7;dat<<=1;printf("%d\r\n", SER);}printf("===========method 3=============\r\n");//方式3dat=0x89;for(i=0; i<8; i++){SER = (dat<<i)>>7;printf("%d\r\n", SER);}return0;}
上面的代码主要是为了实现获取8位无符号数据dat的每一位并写入SER和打印出来,理论上分析似乎都可以实现吧?我们来实际运行看下结果:
![]()
但从上面的实际运行结果来看,只有方式1和方式2的结果是符合预期的,方式3的结果为什么是这样的呢?这是为什么呢?估计很多同仁就比较喜欢以这样一步到位的方法实现功能代码。其实在这里,就涉及到了C语言的整型提升的概念。
如果把代码段:
改成:
复制
SER = (unsigned char)(dat<<i)>>7;//显式类型转换
再运行看下:
![]()
这个时候方式3的代码执行结果也符合预期了。
如果再把代码段:
改成:
复制
SER = (unsigned int)(dat<<i)>>7;//显式类型转换
再运行看下:
![]()
这个时候就和第一次运行的结果一样了,也是不符合预期的。
然后在KeilMCU中运行的结果也是如此:
![]()
通过以上的代码和实测对比,估计很多同仁应该知道了何为整型提升了。
接下来我们就从理论上说一下C语言的整型提升吧,也算是再复习一次。
C语言整型提升(Integer Promotion)的原意是一种隐式类型转换规则,它规定了在表达式中当不同类型的数据进行运算时,较小的整型数据类型会自动转换为较大的整型数据类型。
为什么需要整型提升呢?主要基于下面三个方面的考虑:
效率考虑:CPU通常对int大小的数据操作效率最高;
避免溢出:提升到更大的类型可以减少中间结果溢出的风险;
一致性:确保表达式求值结果的一致性,不受平台差异影响。
那整型提升一般会发生在哪些情况呢?主要包括如下:
当一个表达式中包含比int小的整数类型(如char、short等)时;
在使用位运算符(~、&、|、^)时;
在使用算术运算符(+、-、*、/)时。
提升规则主要如下:
如果原始类型的所有值都可以用int表示,则转换为int;
否则转换为unsignedint。
在代码语句SER= (dat<<i)>>7; 中,涉及了先左移后右移运算,系统按照规则判断dat的最高位是有用的,所以在执行dat<<i时,将dat的最高位保留了。比如dat<<1的值其实变成了unsigned int类型的0000 0001 0001 0010,整个过程通过下图一目了然:
![]()
如果在语句前面加了显示类型强制转换unsigned char,则dat<<1变成了0001 0010,再右移7位后,即为0,与预期结果符合。
综上所述,虽然有时候将几条运算语句合并成一条语句执行运算的操作能够精简代码量,但是因为C语言在数据类型转换这块,其实是比较复杂的,对于类似这种多个运算符参与的逻辑运算,分开多条语句实现比全部用一条语句实现更好,不仅代码可读性更强,也不容易出错。如果你确实需要一步到位,那一定要将变量的数据类型定义正确,或者掌握并灵活运用C语言的显式类型转换和隐式类型转换,否则在项目开发上你一定会吃大亏的~~。
因此,理解整型提升对于避免C语言中的一些微妙错误非常重要,特别是在涉及位操作和混合类型运算时。
---------------------
作者:dffzh
链接:https://bbs.21ic.com/forum.php?mod=viewthread&tid=3477066&_dsign=775d7d07
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。