一、操作符的属性:优先级、结合性
这两个重要的属性决定了表达式求值的计算顺序
1.优先级
例:3 + 4 * 5
就是先计算4 * 5,然后计算3 + 20 结果是23而不是35
因为乘法的运算优先级高于加法
2.结合性
当运算符的优先级相同的时候,就要看结合性来决定先计算谁
分为右结合和左结合,大部分运算法都是左结合(从左往右计算),少数的右结合(从左往右计算),比如赋值运算符(=),将右边的值赋给左边
大家大概记住下面这些运算符的优先级就可以了
• 圆括号( () )
• ⾃增运算符( ++ ),⾃减运算符( -- )
• 单⽬运算符( + 和 - )• 乘法( * ),除法( / )
• 加法( + ),减法( - )
• 关系运算符( < 、 > 等)
• 赋值运算符( = )
(由上至下优先级递减)
由于圆括号的优先级最高,可以使用它改变其他运算符的优先级。
也可以参考下面的链接
https://zh.cppreference.com/w/c/language/operator_precedence
这些优先级和数学的运算逻辑比较相似,可以结合计算
二、表达式求值
1.整型提升
C语言中的整型算术运算总是至少按照默认整型类型的精度来计算的,也就是int
也就是说,在计算char、short类型的数据时也会被转换成int类型计算,计算完成之后再进行截断保留原本类型长度
那这不是脱裤子放屁吗,增长又截断,为什么要这么做?
简单来说:就是CPU中的计算元器件ALU中一般都是int字节长度,在CPU中难以处理的一些数据转换为int类型就可以处理
怎么进行整型提升
1. 有符号整数提升是按照变量的数据类型的符号位来提升的
2. 无符号整数提升,高位补0
//负数的整形提升
char c1 = -1;
变量c1的⼆进制位(补码)中只有8个⽐特位:1111111
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升11char c2 = 1;
变量c2的⼆进制位(补码)中只有8个⽐特位:00000001
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为0
提升之后的结果是:00000000000000000000000000000001
//⽆符号整形提升,⾼位补0
2.算术转换
如果同一操作符的操作数是不同的类型,那么除非统一转化为其中一个类型,不然操作无法进行
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名靠后,那么首先要转换为另外⼀个操作数的类型后执行运算。
3.问题表达式
1. a*b + c*b + e*f
在计算机眼中,这就有两个运算路径
1.先全部算乘法,然后全部算加法
2.先算a*b和c*b 然后两者相加,再算e*f,在相加
2. c + --c
同上,操作符的优先级只能决定⾃减 -- 的运算在 + 的运算的前⾯,但是我们并没有办法得知, + 操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。
假设int c = 5
那么第一个c可能是5 也可能是4
3.如图
#include <sdtio.h>
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf( "%d\n", answer);//输出多少?
return 0;
}
我们虽然可以知道先算乘法再算加法,但是先调用那个函数我们是无法知道的
4.总结
即使有优先性和结合性,表达式依然可能具有不同的计算路径,造成歧义,所以我们不要写出不负责的表达式,也不要写很复杂的表达式,代码不是越简单越好,而是要让人看清,用一个表达式表示过多的信息,这个表达式除了计算机和本人就没有人能知道是什么了。而且时间一长可能本人都忘记是为什么写出来了。