带小白了解C语言中操作符的属性:优先级、结合性

1.操作符属性介绍

1.1优先级

优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的。

3 + 4 * 5

上⾯⽰例中,表达式3 + 4 * 5⾥⾯既有加法运算符(+),⼜有乘法运算符(*)。由于乘法的优先级⾼于加法,所以会先计算4 * 5,⽽不是先计算3 + 4。

1.2结合性

如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符(=)。

5 * 6 / 2;

‘*’ 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算5 * 6,再计算6 / 2。
运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列),建议⼤概记住这些操作符的优先级就⾏,其他操作符在使⽤的时候查看下⾯表格就可以了!
在这里插入图片描述

圆括号( )
⾃增运算符、⾃减运算符+ +、- -
单⽬运算符+ 和 -
乘法、除法* 和 /
加法、减法+ 和 -
关系运算符<、>等
赋值运算符=

由于圆括号的优先级最⾼,可以使⽤它改变其他运算符的优先级。

在这里插入图片描述

参考:具体请点击

2.问题表达式解析

2.1表达式1

1 //表达式的求值部分由操作符的优先级决定。
2 //表达式1
3 a*b + c*d + e*f

表达式1在计算的时候,由于 * 比 + 的优先级高,只能保证, * 的计算是比 + 早,但是优先级并不能决定第三个 *比第一个 +早执行。
所以表达式的计算机顺序就可能是:

//情况一
1 a*b
2 C*d
3 a*b + c*d
4 e*f
5 a*b + c*d + e*f
//情况二
1 a*b
2 c*d
3 e*f
4 a*b + c*d
5 a*b + C*d + e*f

此时有的同学就会说?这搞来搞去不还是结果一样吗?我想说的是,这只是简单的a, b, c当你把他们全部换成有关联的表达式的时候,计算的先后顺序对结果的影响是很大的!所以,我们平时写代码的时候多关注一下,不要写出有歧义的代码。

2.2表达式2

1 //表达式2
2 c+--C;

同上,操作符的优先级只能决定自减 --的运算在+的运算的前面,但是我们并没有办法得知,+ 操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。

2.3表达式3

1 #include <stdio.h>
2
3 int fun()
4
5 	static int count=1;
6 	return ++count;
7 }
8 int main()
9	int answer;
10 	answer=fun()-fun()*fun();
11 	printf("%d\n",answer);//输出多少?return ;
12 }

这个代码有没有实际的问题?有问题!虽然在大多数的编译器上求得结果都是相同的。但是上述代码 answer= fun()-fun()* fun();中我们只能通过操作符的优先级得知:先算乘法,再算减法。
但是:函数的调用先后顺序无法通过操作符的优先级确定。
分析:fun()函数中,由于count变量是static修饰的,当调用函数结束后,count不会被销毁,继续保存原来的值参与下一次函数调用,但是函数的调用顺序不确定就有如下两种结果:

1.2 - 3 * 4
2.4 - 2 * 3

2.4表达式4

//表达式4
#include<stdio.h>
int main(){
	int i = 1;
	int ret=(++i)+(++i)+(++i);
	printf("%d\n", ret);
	printf("%d\n",i);
	return 0;
}

VS2022运⾏结果:

这里是引用

gcc编译器执⾏结果:

这里是引用

看看同样的代码产生了不同的结果,这是为什么?简单看一下汇编代码,就可以分析清楚
这段代码中的第一个 + 在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个+和第三个前置 ++的先后顺序。

  1. VS2022是先把 ++i 执行完成后,i变成4再三个一起加,最终结果是12
  2. gcc编译器是先执行前面的加法,因为前置自增的优先级比加法高-------->(1 + 1)+ (2 +1)
    =5可得前半部分是5,然后再计算后半部分 5 + (3 + 1 )= 9。得出最终结果!

2.5总结

即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在潜在风险的,建议不要写出特别复杂的表达式。

3.实战错误分析

在这里插入图片描述
题目链接:牛牛的并联电路
很多新手朋友,做这道题的时候,很容易就写出来了问题1的代码。这种其实对于新手朋友们来说很正常,刚开始按着数学思维来做,导致错误。下面我们来进行分析

3.1算术转换

要先了解这道题!我们先来了解什么是算术转换。
如果某个操作符的各个操作数属于不同的类型,那么除⾮其中⼀个操作数的转换为另⼀个操作数的类型,否则操作就⽆法进⾏。下⾯的层次体系称为寻常算术转换。

1  long double
2  double
3 float
4 unsigned long int
5 long int
6 unsigned int
7 int

如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算。

3.2问题代码

#include <stdio.h>

int main() {
    int r1, r2 = 0;
    scanf("%d %d", &r1, &r2);
    float  r3 = 0;
    r3 = 1  / ((1 / r1 * 1.0) + (1 / r2 * 1.0));
    printf("%f",r3);
    return 0;
}

运行展示:

这里是引用

// 原代码
r3 = 1 / ((1 / r1 * 1.0) + (1 / r2 * 1.0));
//          ↑        ↑
//        先做整数除法 再做乘法转浮点

错误分层解析

运算步骤具体计算(假设输入83 86)结果类型问题描述
1/r11/83 → 0int整数除法直接截断小数部分
0 *1.00*1.0=0.0float此时补转浮点但数值已丢失
1/r21/86 → 0int同理整数除法错误
分母总和0.0 + 0.0 = 0.0float分母为零

这里的关键在于运算顺序和类型提升规则。在这道题中,因为括号() 的优先级最高,先计算括号里面的。初学者很容易写出这样的代码,认为先把分母转换成float类型,1 / 分母就不会截断小数部分。事实上,C语言中,乘法和除法的优先级相同,优先级相同就要看什么?结合性!乘法和除法结合顺序都是从左到右,所以先计算1 / r1,这里两个整数相除,结果仍然是整数除法。例如,当r1=83时,1/83的结果是0,然后再乘以1.0,变成0.0。同理,1/r2也是0,再乘以1.0得到0.0。因此分母部分实际上是0.0 +0.0 =0.0,导致整个表达式变为1除以0.0,结果为无穷大(inf)。
正确的修改方法应该是在进行除法运算之前就确保至少有一个操作数是浮点数。例如,将1改为1.0,或者在除法前对操作数进行强制类型转换。这样,除法运算本身就会执行浮点除法,而不是整数除法。例如:

r3 =1.0 / ((1.0 / r1) + (1.0 / r2));

或者使用更简洁的公式

r3 = (r1 * r2) / (float)(r1 + r2);

后言

这就是操作符的剖析。帅哥美女自己好好消化!今天就分享到这!感谢各位的耐心垂阅!咱们下期见!拜拜~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值