表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。有些表达式的操作数在求值的过程中可能需要转换为其他类型。
隐式类型转换
1.整形提升
偷偷的转换的,并不能直观的看到
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。
int main()
{
char a = 3;
//00000011
//00000000000000000000000000000011 a提升后
char b = 127;
//01111111
//00000000000000000000000001111111 b提升后
char c = a + b;//a和b要发生整形提升
//00000000000000000000000010000010--相加后
//存到c里面只能存放8个比特位:
//10000010-c c现在要%d整形打印,c要提升
//c提升后:
//11111111111111111111111110000010-补码
//11111111111111111111111110000001
//10000000000000000000000001111110
//-126
printf("%d\n", c);
return 0;
}
a和b都不够int大小,所以要进行整形提升,a和b的值被提升为普通整型,然后再执行加法运算。
加法运算完成之后,结果将被截断,然后再存储于c中。
如何整形提升?
整形提升是按照变量的数据类型的符号位来提升的。
//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
11111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
整形提升的例子:
int main()
{
char a = 0xb6;//二进制序列:10110110
short b = 0xb600;//1011011000000000
int c = 0xb6000000;//10110110000000000000000000000000
if(a==0xb6)//比较大小也是运算,这里发生整形提升
printf("a");
if(b==0xb600)//比较大小也是运算,这里发生整形提升
printf("b");
if(c==0xb6000000)//c是整形,不用提升,就可以打印
printf("c");
return 0;
}
if判断语句里要进行比较大小运算,a是char类型,b是short类型,所以a和b要进行整形提升,整形提升后发生了改变,c不需要整形提升,所以可以打印
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}
sizeof括号中放的表达式是不参与运算的!我们假想c如果进行了运算,推算表达式所产生的类型是什么,发生整形提升,表达式的类型属性为int,所以为4个字节。
2.算术转换
比如一个int和一个long进行运算,那么这个int转换为long进行运算,小的转换成大的进行运算
操作符的属性
表达式的三个影响因素
-
操作符的优先级
优先级在相邻两个操作符不一样时,看优先级
-
操作符的结合性
在相邻两个操作符一样时,看结合性
-
是否控制求值顺序。
我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题
的。
一些问题表达式
代码1
//表达式的求值部分由操作符的优先级决定。
//表达式1
a*b + c*d + e*f
代码1在计算的时候,由于比+的优先级高,只能保证*的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行
a*b
c*d
a*b + c*d
e*f
a*b + c*d + e*f
或者:
a*b
c*d
e*f
a*b + c*d
a*b + c*d + e*f
如果a、b、c、d、e、f是表达式,那么运算顺序不确定就是个大问题了。
代码2
//表达式2
c + --c;
同操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。
代码3
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
这个代码在不同编译器的运行结果是不一样的。
代码4
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int ret;
ret = fun() - fun() * fun();
printf( "%d\n", ret);//输出多少呢?
return 0;
}
函数的调用先后顺序无法通过操作符的优先级确定
代码5
#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
//尝试在linux 环境gcc编译器,VS2019环境下都执行,看结果。
Linux环境下编译结果为10
vs2019环境下编译结果为12