上一篇文章传送门:操作符上
前言:上期我们介绍了C语言的操作符的使用方法,这期我们主要侧重讲当我们已经了解了操作符的基本知识后怎样样来看待运算路径的问题。
一,优先级和结合性
上期我们介绍了很多操作符,包括像算数操作符,位操作符,移位操作符等。但还有一个问题就是我们虽然已经知道这些操作符怎么用但却不知道它们是按照什么顺序来运算的。这就要讲到操作符的2个属性——优先级和结合性,优先级和结合性就是用来解决这一问题的。
1,优先级
举个例子:3+4*5
看到这段代码我们自然而然的就会想到先算乘除再加减,这是基于我们在数学上学过这个优先级才知道的。但C语言操作符有很多是我们没学过的怎么办呢?别担心,我有一张表可以让你一览无余:
这个表格能很清晰的告诉我们优先级的顺序,优先级很明显就是在一个表达式中谁优先级高谁就先运算。 但是这又会产生一个问题,就是如果表达式中所有操作符的优先级都相同那怎么看呢? 这就要看结合性了。
2,结合性
前面我们说了,当一个表达式中所有的操作符的优先级都一致的时候是无法判断先算哪个了;这时候就要看结合性了,看是左结合(从左到右执行)还是右结合(从右向左执行)。
列如:3 * 5 / 2
我们知道乘除的优先级是相同的这时候看结合性,我们通过观察上面的表格就会发现他们都是左结合的所以是从左到右去计算。
下面是部分优先级的结合顺序(从高到低)注意大概记住就行,实在记不住可以查表:
• 圆括号( () )
• ⾃增运算符( ++ ),⾃减运算符( – )
• 单⽬运算符( + 和 - )
• 乘法( * ),除法( / )
• 加法( + ),减法( - )
• 关系运算符( < 、 > 等)
• 赋值运算符( = )
实在不行的话读者可以在写代码的时候直接用括号就行,括号的优先级最高。
这里我们给出C语言关于优先级的参考:C语言的优先级。
看完了优先级和结合性紧接着我们就可以来看表达式了:
二,表达式求值
1,整型提升
各位读者第一次看到这个概念可能回比较的懵,我们直接拿出一段代码来举例:
#include<stdio.h>
int main()
{
char a = 10;
char b =120;
char c = a+b;
printf("%d",c);
}
我i们来看结果为什么输出的-126呢?按照常理120+10应该130才对啊,怎么会出现个-126呢?要回答这两个问题就要涉及到整型提升了。我们来看看整型提升的概念
整型提升说的是在C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进⾏的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
比如上面代码中char a和char b 在运算的时候直接被提升为普通整型, 然后再进行加法计算,计算的结果被阶段存在c中。
那如何进行整形提升呢?
负数的整形提升:
char a=-1
变量a的二进制位(补码)只有8个比特位 因为char类型占1个字节8比特位。 11111111
因为char a为有符号的char,所以再整形提升的时候在剩余的24个字节中补充符号位 即1
整形提升后为:11111111 11111111 11111111 11111111正数的整形提升:
char b=1
变量a的二进制位(补码)只有8个比特位 因为char类型占1个字节8比特位。 00000001
因为char a为有符号的char,所以再整形提升的时候在剩余的24个字节中补充符号位 即0
整形提升后为: 00000000 00000000 00000000 00000001
了解完正负数的整形提升之后我们回过头来分析上面代码的结果。
首先我们用的是%d占位符是打印10进制整数的占位符,其次从图不难看出char类型的取值范围为-128~127,所以10+120是不可能得到130的。这就解决了我们上面的疑问。
这时又要有人问了如果用的是不同类型运算呢?会怎么样?那就要涉及算术转换了。
2,算术转换
算术转换说的是如果某个操作符的各个操作数属于不同的类型,那么除⾮其中⼀个操作数的转换为另⼀个操作数的类 型,否则操作就⽆法进⾏。
下⾯的层次体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名靠后,那么首先要转换为另外⼀个操作数的类型后执行 运算。比如 int a=10 double b =20.0 那么a+b运算时由于double排名靠前所以int a 会被转化成double a。
然后再进行计算 。
当我们了解这些基本的知识以后我们就可以来看一些表达式了。
三,表达式解析
表达式1:a*b+c*d+e*f
这个表达式的运算路径是什么呢?
首先因为我们知道表达式的求值部分由优先级来决定,所以我们只知道先乘再加是不知道哪个乘法先算,所以造成运算循序有很多种。比如
这里就不一一列举。
表达式2: c + --c;
这段代码歧义就是我们不知道第一个c是使用 - - c后的值还是 - - c之前的值来计算。比如c=5 c+ -- c
可能是5加4也可能是4加4所以这就存在歧义。
表达式3:
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
这种其实就是错误代码,因为其在不同的编译器下有不同的结果,一定要避免写出这种代码!
表达式4:
#include <stdio.h>
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer; answer = fun() - fun() * fun();
printf( "%d\n", answer);//输出多少?
return 0;
}
但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先 算乘法,再算减法,函数的调用先后顺序无法通过操作符的优先级确定。
表达式5:
#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
ret为12为什么会得到12我们来看看反汇编代码就明白了
这段代码的反汇编代码实际上就一直在执行++i这个操作当每个i加到4后再去执行三次加法,所以4+4+4=12结果就是这么得到的。 但实际上将这段代码复制到devc++上运行结果也是不同的,大家可以试试。
这段代码中的第⼀个 + 在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第⼀个 + 和第三个前置 ++ 的先后顺序。
所以这种代码也是有争议的大家要避免写出这种代码!
注意:有关结构的操作符将在结构章节进行介绍!
以上就是操作符的所有内容啦!
感谢能够看到这里的读者,如果我的文章能够帮到你那我甚是荣幸,文章有任何问题都欢迎指出!制作不易还望给一个免费的三连,你们的支持就是我最大的动力!