前言
写这篇就为了再遇到稍微复杂一点的表达式不至于绕晕;优先级和结合方向不再赘述,随便一搜就都有了,在优先级和结合方向上我借鉴的是这篇;
注意,虽然有优先级和结合方向一说,但是程序不止是给机器编译的,也是给人看的,做个人,该括括号的时候,尽量提高可读性吧!
如有错误欢迎评论指出。
故事的开端
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);
开始分析:
也就是说:
- 先从左到右把两个函数调用;
- 再对fill返回的地址进行取值;
- 再把alloc_frame函数返回的值赋给fill返回地址的取值。
和GDB跟的结果一样,那故事就在这结束吧。
总结
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);