类型转换下编译器偷偷做的事————整形提升(Inter Promotion)
2017-03-03 12:55
423 查看
一.前言
在讲述这个话题之前,还是先举几个代码例子: |
long long ll = 1; long l = 2; double db = 3; float f = 4; unsigned int uint = 5; decltype(db + ll) // ->类型? decltype(uint + f) // ->类型? decltype(uint + l) // ->类型?
signed char a = 0xe0; unsigned int b = a; unsigned char c = a; bool b1 = (a > 0 && c > 0); // b1 true ? false ? bool b2 = (a == c); // b2 true ? false ? printf("%08x",b); // what's the output ?
//小端 unsigned int a = 0xFFFFFFF7; unsigned char i = (unsigned char)a; char* b = (char*)&a; printf("%08x %08x",i,*b); //what's the output ?
二.隐式转化规则
隐式转化发生在很多地方:表达式计算、赋值、实参到形参、函数返回值等等。这里不一一介绍。这里主要是介绍表达式计算中隐式转换相关的问题,在二元操作中,如果两个操作数类型不一样就会发生隐式类型转换,而一般的转化规则如下:(从上到下,序号越小优先级越高) |
2.判断是否有操作数为double类型,如果有将另一个操作数转化为double类型
3.判断是否有操作数为float类型,如果有将另一个操作数转化为float类型
4.两个操作数皆为整形,进行Inter Promotion,按照int,unsigned int,long,unsigned long,long long,unsigned long long,进行找到一个最小且能够存放两个操作数对应类型的所有值的类型。
可以看出来无论整形操作数类型字节多大,一旦有浮点数类型参与,为了保证浮点数精度,都会转化为浮点数类型。 或许,有的人在不知晓Inter Promotion提升规则之前,面对Inter Promotion有过误解:在两个整形相运算时候,一个低位宽的类型总是向另一个高位宽的类型转换,然而这是不正确的。因为在Inter Promotion的过程中隐式转换结果类型甚至可以不为两个操作数类型的任意一个。 比如,32位机器上:unsigned int + long -> unsigned long。 要正确理解上面这个转化,我们需要细细品味第四个规则的那句话:”最小且能够存放两个操作数对应类型的所有值的类型”,b类型能存放a类型所有值的意思就是对于a类型的任意一个值,b类型都能表达出且不会溢出。 所以,上面的转换中,假如我们尝试将目标类型定为long。由于我们知道在32位机上long和unsigned int都占4字节,所以很显然对于unsigned int类型中大于2^31-1的正值,long 类型都无法表达出。所以需要将目标类型提升为 unsigned long 。 那么第一个段代码的答案就很显然了: |
decltype(db + ll) // ->db decltype(uint + f) // ->float decltype(uint + l) // ->unsigned long
三.Inter Promotion
为什么再单独将Inter Promotion拿出来讲一节,我是想再从内存的角度去简单了解一下Inter Promotion时编译器偷偷做了什么事情。 为了便于阐述,我还是将整形字面常量的情况拿出来说一下: |
//type 为某整形类型 type a = 123; a = 456; type b = 123*123; b = 456 * 789;
上面的整形字面常量参与了这样几个动作:初始化、赋值、运算。那么在这一系列过程中,类型发生了如何的转换? |
如果整形字面常量参与运算,那么会按照以下处理(不考虑负号,负号只是对原立即数做一次负数补码操作):
MSVC | GNU C++ | |
---|---|---|
0 ~ 2147483647(2^31-1) | int | int |
2147483648(2^31-1) ~ 4294967295(2^32-1) | unsigned long | long long |
4294967295(2^32-1) ~ 9.22*10^18(2^63-1) | long long | long long |
9.22*10^18(2^63-1) ~ 1.84*10^19(2^64-1) | unsigned long long | __int128 |
整形提升(Inter Promotion)则是指目标类型位宽大于源类型位宽。这样的情况除了将源类型位宽放置于目标类型的低位,还需要对目标类型的高位进行填充。而填充规则则是:
如果源类型为unsigned类型,则用0填充高位。
如果源类型为signed,则用源数据符号位的值填充高位。
依照上面的规则,我们再来看看文章最开始所提及的题目: |
signed char a = 0xe0; unsigned int b = a; unsigned char c = a; bool b1 = (a > 0 && c > 0); // b1 true ? false ? bool b2 = (a == c); // b2 true ? false ? printf("%08x",b); // what's the output ?
b = a -> 进行类型转换,源类型位宽小于目标类型,进行整形提升。源类型为signed,那么用符号位填充目标类型高位。0xe0符号位为1,所以填充后b的十六进制为 : 0xffffffe0。 c = a -> 进行类型转换,源类型位宽和目标类型位宽一样,所以在内存中a,c的数据其实是一样的。 来看第一个:bool b1 = (a > 0 && c > 0),根据上文(二.隐式转换规则)的第4点,我们知道a,c 都会隐式提升到int,再来看整形提升的规则,我们可以知道a是signed且符号位为1,所以提升到 int 为 0xffffffe0, 而c是unsigned,所以提升到 int 为 0x000000e0。所以知道 a < 0,c > 0 。那么 b1 = false; 第二个,和第一个一样,根据上文(二.隐式转换规则)的第4点,我们知道a,c 都会隐式提升到int,a : 0xffffffe0 ,c : 0x000000e0,a != c。则b2 = false。 第三个,上面已经说了b的十六进制为 : 0xffffffe0。所以输出为 ffffffe0。 |
unsigned int a = 0xFFFFFFF7; unsigned char i = (unsigned char)a; char* b = (char*)&a; printf("%08x %08x",i,*b); //what's the output ?
i = (unsigned char)a,在类型转换过程中发生了截断,因为是小端存储,所以 i 的十六进制为 0xF7。 哦,这里我要先说说%x,%x的定义:使用十六进制数字0F的无符号十六进制整数x。换句话来说,它会把源数据转化为unsigned int然后再将其十六进制数据输出。 根据上面所述的规则,i是unsigned所以0填充,结果为000000f7,然而b是signed,所以用符号位填充,结果为fffffff7。 有人可能会说i,*b内存数据不是一样的吗,怎么按照同一个格式输出结果不一样?那是因为i,*b的数据符号类型不一样导致,i,*b在整形提升过程中,高位填充不一样。当然我们也可以不进行整形提升直接输出源内存十六进制值: |
unsigned int a = 0xFFFFFFF7; char i = (char)a; char* b = (char*)&a; printf("%hhx %hhx", i, *b); // f7 f7
这样输出结果就都为f7了。 |
相关文章推荐
- 编译器错误信息: CS0716: 无法转换为静态类型“System.Web.SiteMap”
- java中不同类型的转换和提升
- AVA学习笔记之变量类型转换和类型自动提升
- c++算术运算和位运算中类型转换和类型提升规则和方法
- QT类型转换之整形转字符串
- java中不同类型的转换和提升
- C语言中的数值类型转换:整型提升/寻常算术变化/截断/强制类型转换
- 【备忘录】c++ 整形浮点<=>字符串 类型转换
- 关于C语言中的 " 类型提升 "(type promotion)
- 【VS2010学习笔记】【类型转换】整形转换为CString类型
- sql中数据类型的转换(自己写比较累哈,偷偷懒,转下别人的)
- C语言 sizeof 整型提升 类型转换
- C# 的数据类型转换 c#字符串转换格式 c#日期数字字符串整形格式转换
- .编译器错误信息: CS0030 无法将类型“ASP.login_aspx”转换为“System.Web.UI.WebControls.Login”
- vc2005中对无符号与有符号数相加时,自动转换数值类型时,对于短整形与整形的差别。
- JAVA基础学习之String、StringBuffer、StringBuilder、基本数据类型的使用、整形进制转换、集合Collection、Vector、ArrayList、LinkedList、HashSet、TreeSet等(3)
- 编译器错误信息: CS0030: 无法将类型“ASP.webusercontrol_treeview_ascx”转换为“System.Web.UI.WebControls.TreeView”
- c++中类型提升和转换
- C语言中的算数转换和整形提升
- 强制类型转化 转换是否成功不属于编译器的管辖范围,不合适的类型会让你得到运行时异常