UP | HOME

C语言之过

目录

挑了一些 C 语言中的容易被人忽略的细微之处,C语言的新手常常在这些问题上想当然,其结果自然是 BUG。

错误的优先级

优先级问题 表达式 人们可能误以为的结果 实际结果
.的优先级高于*。 *p.f p 所指对象的字段 f 对 p 取 f 偏移,作为指针,然后进行解除引用操作。
[]高于* int *ap[] ap 是个指向 int 数组的指针 ap 是个元素为 int 指针的数组
函数()高于* int *fp() fp 是个函数指针,所指函数返回 int。 fp 是个函数,返回 int *
==和!=高于位操作符 (val & mask != 0) (val & mask) != 0 val & (mask != 0)
==和!=高于赋值符 c = getchar() != EOF (c = getchar()) != EOF c = (getchar() != EOF)
算术运算高于移位运算符 msb << 4 + lsb (msb << 4) + lsb msb << (4 + lsb)
逗号运算符在所有运算符中优先级最低 i = 1, 2 i = (1, 2) (i = 1), 2

这些运算符的优先级问题都值得我们好好想想,并牢记在脑海中。对 C 语言的声明有疑问的,比如说上面表格中的二、三行,可以参考另一篇笔记《C语言复杂声明分析》

我们在此再讨论一下上面表格中的最后一行,涉及逗号的情况有时会让程序员歇斯底里。例如,

    i = 1, 2;

我们知道逗号运算符的值就是最右边操作数的值。但在这里,赋值符的优先级更高,所以实际情况应该是:

    (i = 1), 2; /* i的值为1 */

i 赋值为 1 接着执行常量 2 的运算,计算结果被丢弃。最终,i的结果是 1 而不是 2。

有些专家建议在 C 语言中牢记两个优先级就够了:乘法和除法先与加法和减法,在涉及其他的操作符时一律加上括号。


结合性

操作符的优先级已经够让人心烦的了,许多人对操作符的结合性同样感到困惑。在标准 C 语言的文档里,对操作符的结合性并没有作出非常清楚的解释。

总结起来,倒比较简单。所有的赋值符(包括复合赋值符)都具有右结合性,就是说表达式中最右边的操作最先执行,然后从右到左依次执行。类似地,具有左结合性的操作符(如位操作符=&=和=|=)则是从左至右依次执行。如果在计算表达式中的值时需要考虑结合性,那么最好把这个表达式一分为二或者使用括号。


最大一口策略

ANSI C 规定了一种逐渐为人所熟知的“maximal munch strategy(最大一口策略)”。这种策略表示如果下一个标记有超过一种的解释方案,编译器将选取能组成最长字符序列的方案。如:

    z = y+++x;

程序员的可能是 z = y + ++x ,但也可能是 z = y++ + x 。根据最大一口策略,上面这个例子会被解析为 z = y++ + x 。再如:

    z = y+++++x;

按照最大一口策略会被解析为 z = y++ ++ +x ,这将引起一个编译错误,而我们实际上想要的结果为 z = y++ + ++y 。当然需要这种情况最好的办法是用空格或和括号将标记隔开,这样一开就不会有任何异议也不会出现编译错误了。

作者: Petrus.Z

Created: 2021-09-01 Wed 00:38