一文解决C语言运算符的优先性与结合性——Think like a computer

前言

写这篇就为了再遇到稍微复杂一点的表达式不至于绕晕;优先级和结合方向不再赘述,随便一搜就都有了,在优先级和结合方向上我借鉴的是这篇
注意,虽然有优先级和结合方向一说,但是程序不止是给机器编译的,也是给人看的,做个人,该括括号的时候,尽量提高可读性吧!
如有错误欢迎评论指出。

故事的开端

	for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i ++)
		*xsk_ring_prod__fill_addr(&xsk_info->umem->fq, idx++) =
			xsk_alloc_umem_frame(xsk_info);

第一眼看过去,你知道执行顺序吗?嗯……第一眼觉得很简单,然后有一点犹豫,因为不知道为什么会这样执行,只是觉得就该这样。
然后就有了这篇文章,不止自己要清楚,还要给别人讲清楚。

什么是运算符的优先级和结合方向

优先级

先拿简单例子来讲:

*p++;

我们知道自增运算符++的优先级与取值运算符*相同,恰好是同优先级的特例,同优先级看结合方向(同优先级结合方向是一致的),那么把自己想象成计算机,看向这条语句,我们只解析,不纠错:

注:随便抄的一个例子,里面说自增符++的优先级高于取值符* 仔细一看发现说的错的,优先级根本是一样的,只好先说同优先级的例子;

Think like a computer:
> 我们从高优先级开始扫描,其他部分统一视作一个"块",发现优先级2:
> [*] [块0] [++]
> 按结合方向处理,2优先级的从右往左,也就是先处理右边的[++]
> [++]单目运算符,直接往左;
> 从[++]往左,到[块0],再撞到运算符[*],好了停下来,我们结合一下:
> [*]([块0]++),恰巧[块0]里面没有关键字符要处理了,直接:
> [*](p++)
> 到这里人已经一眼可以看出来结果了,可是计算机要再处理一下:
> 处理下一个优先级2的运算符,括号外往左,到[*],右结合,向右看,只有一整个块(p++),所以:
> (*(p++)),整个语句已成块,工作结束;

补一张图示:
在这里插入图片描述

所以上面那个例子应该等效为:

*(p++);

从可读性上来讲,还是后者更好一些;

结合方向

从右往左意味着:

  • 同优先级下处理顺序从右往左(见上个例子);
  • 双目运算符先向右处理;
  • 单目运算符直接往有效方向处理,无效方向括号封住;(如上个例子,但按照语言的设计,就算先向右处理应该也无伤大雅)

还是先简单例子:

a=b=c

赋值运算符=是从右往左,也就是先右后左;

Think like a computer:
> 我们从高优先级开始扫描,其他部分统一视作一个"块",发现优先级14:
> [块0] [=]① [块1] [=]② [块2]
> 按结合方向处理,14优先级的从右往左,也就是先处理右边的[=]②
> [=]②双目运算符,先看向右,右边的[块2],再撞到尽头,结束,画上右括号标记一下,恰好没有关键字符要处理了,偷个懒直接处理成c
> [块0] [=]① [块1] [=]② c)
> 再从[=]②向左,到[块1](顺便处理成b),再撞到运算符[=]①,好了停下来,我们画上左括号标记结合:
> [块0] [=]①(b = c)
>偷个懒,用箭头和括号代替接下来的文字描述:
> [=]① --> (b = c) --> 尽头
[块0] [=]①(b = c))
> 尽头 <-- [块0] <-- [=]①
([块0] [=]①(b = c))
> END

再来练一个例子,再次偷懒,还是画图快:

f1() * f2() + f3();

在这里插入图片描述

故事在这能结束吗?

回到一开始的例子:

*xsk_ring_prod__fill_addr(&xsk_info->umem->fq, idx++) =
			xsk_alloc_umem_frame(xsk_info);

简化一下:

*fill(&fq, idx++) = alloc_frame(info);

开始分析:
在这里插入图片描述
也就是说:

  1. 先从左到右把两个函数调用;
  2. 再对fill返回的地址进行取值;
  3. 再把alloc_frame函数返回的值赋给fill返回地址的取值。

和GDB跟的结果一样,那故事就在这结束吧。

总结

Think like a computer:

  1. 先处理高优先级运算符,同优先级按结合方向处理;
  2. 按照结合方向,从运算符延伸,撞到其他运算符或语句尽头即停止,画上分割括号;
  3. 括号运算符/分割括号内看做一个整体,单独讨论;

这种方法仅起一个帮助作用,能够让初学者遇到复杂式子时有一个范式,能够理解复杂语句执行的顺序。但真要快速理解复杂的代码,还得靠多年的经验和实际的调试。

吐槽

这段代码加个括号瞬间就没什么事情了:

	for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i ++)
		*(xsk_ring_prod__fill_addr(&xsk_info->umem->fq, idx++)) =
			xsk_alloc_umem_frame(xsk_info);
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WuPeng_uin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值