最近在看模板元编程的时候遇到这么一件事: char +(or -) char => int short +(or -) short =>int 为什么两个短整型相加减的类型是一个int呢?
这里涉及到一个概念 叫做类型提升,那什么是类型提升,为什么要进行类型提升,于是我展开了实验。 我的代码如下:
template <typename T1, typename T2>
struct plus_result {
using type =
decltype(std::declval<T1>() + std::declval<T2>());
};
template <typename T1, typename T2>
using plus_result_t = plus_result<T1, T2>::type;
int main() {
plus_result_t<char, char> a;
plus_result_t<char, short> b;
plus_result_t<short, short> c;
std::cout << typeid(a).name() << std::endl;
std::cout << typeid(b).name() << std::endl;
std::cout << typeid(c).name() << std::endl;
return {};
}
输出:
i
i
i
下面是x86-64 gcc 12.2生成的汇编
看一下下面一段代码,结果会是多少:
char b = 100;
char c = 200;
auto i = b + c;
printf("%s\n", typeid(i).name());
printf("%d\n", i);
printf("%d", sizeof i);
输出结果为:
i
44
4
x86_64指令集masm汇编如下:
mov BYTE PTR [rbp-0x1],0x64 ;0x64
mov BYTE PTR [rbp-0x2],0x64 ;0xc8
movsx edx,BYTE PTR [rbp-0x1]
movsx eax,BYTE PTR [rbp-0x2]
add eax,edx
mov DWORD PTR [rbp-0x8],eax
将十六进制立即有符号数100(0x64 -> 0110 0100) 放入[b]所指向的对象b的内存的第1个字节 将十六进制立即有符号数200(== -56, -56是有符号数,计算机中以其补码存放(1100 1000 == c8)) 放入[c]所指向的对象c的内存的第1个字节
从b中取出一字节放到32 位dx寄存器edx中,同样从c中取出1字节放到32 位ax寄存器eax中 这里使用movsx(带符号扩展指令),当计算机中存放一个带符号数时,符号位位于最高位。对于正数而言,会把寄存器中扩展后高位置为0,其效果和movzx(零扩展指令)一样。而对于负数,高位会被全部置为1。 因此寄存器edx为0000 0000, 0000 0000, 0000 0000, 0110 0100 -> 0x00000064 ; eax中数据不再为1101 0110,而是1111 1111, 1111 1111, 1111 1111, 1100 1000 -> 0xFFFFFFC8 加和为 1(舍弃), 0000002c
加和后寄存器eax中值为0x0000002c ,取其双字(4字节 )放到对象i中,i为44
生成汇编,i是一个4字节
为什么会生成这样的汇编呢?
C99/C11标准之常用算术转换一章: If the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
自动类型转换的基本规则:
-
在表达式中,char 和 short 类型的值,无论有符号还是无符号,都会自动转换成 int 或者 unsigned int(如果 short 的大小和 int 一样,unsigned short 的表示范围就大于 int,在这种情况下,unsigned short 被转换成 unsigned int)。因为它们被转换成表示范围更大的类型,故而把这种转换称为“升级(promotion)”。 -
按照从高到低的顺序给各种数据类型分等级,依次为:long double, double, float, unsigned long long, long long, unsigned long, long, unsigned int 和 int。这里有一个小小的例外,如果 long 和 int 大小相同,则 unsigned int 的等级应位于 long 之上。char 和 short 并没有出现于这个等级列表,是因为它们应该已经被升级成了 int 或者 unsigned int。 -
在任何涉及两种数据类型的操作中,它们之间等级较低的类型会被转换成等级较高的类型。 -
在赋值语句中,= 右边的值在赋予 = 左边的变量之前,首先要将右边的值的数据类型转换成左边变量的类型。也就是说,左边变量是什么数据类型,右边的值就要转换成什么数据类型的值。这个过程可 能导致右边的值的类型升级,也可能导致其类型降级(demotion)。所谓“降级”,是指等级较高的类型被转换成等级较低的类型。
而且现在32位或者64位CPU一次处理32位或64位数据,对于低于其的数据访问意义不大。
|