最近使用C语言写MIT 6.S081课程的作业,发现自己对于C语言中的类似*,&,[],->符号的运算优先级和结合顺序问题理解并不清楚,这篇文章就是要彻底的理解这些个符号的运算优先级和代表的意义,保证以后凡是看到这样的符号,就都不会心里发慌。
首先,我们要明确,C语言的符号优先级(Precedence)规定了运算符(Operator)的运算顺序,优先级高的符号会先进行运算,然后才轮到优先级低的符号运算。只有对于相同符号优先级的两个运算符,此时讨论结合顺序才是有意义的。结合顺序(Associativity)规定了(在相同优先级符号的情况下)先计算哪个符号的问题。
在同等优先级(Precedence)的情况下,所有的运算符结合顺序(Associativity)一定相同(都是从左到右或者都是从右到左)
举例说明优先级不同造成的区别:
// 由于优先级 * 高于 + 高于 =  | 
优先级相同情况下,结合顺序造成的区别:
// 由于+,-的结合顺序是从左到右  | 
关于前缀和(Prefix ++)以及后缀和(Suffix ++)的问题,可以参考这篇文章显微镜下的 i++ 与 ++i的内容,简而言之就是:
前缀和 ++i:先将局部变量表中的i值+1,再将i放入操作数栈中
后缀和 i++:先将局部变量表中的i放入操作数栈中,再将局部变量表中的i值+1
这其中,操作数栈中的数值就是后续运算(比如说赋值运算int a = i++;)中要操作的数值,因此我们也可以说:
前缀和 ++i:变量i的值+1,整个表达式(++i)的值(操作数栈中存入的值)是变量i的新值
后缀和 i++:变量i的值+1,整个表达式(i++)的值(操作数栈中存入的值)是变量i的原始值
当然,对于指针类型的变量(比如说int *p),前缀和(++p)和后缀和(p++)每一次操作就不是+1了,而是+sizeof(*p)
例如,(++p)->len先执行p的+sizeof(*p)的操作,然后再返回这个新p的len值;(p++)->len则返回的是原始p的len值,然后再执行p的+sizeof(*p)的操作
全部的运算符优先级可以在网上搜索C language operator precedence得到,这里仅列出本文关注的若干运算符:
| Operator | Description | Associativity | 
|---|---|---|
| [] | Array subscript | left to right | 
| -> | Member selection via pointer | - | 
| ++ | Suffix increment | - | 
| () | Function call | - | 
| * | Dereference | right to left | 
| & | Address (of operand) | - | 
| ++ | Prefix increment | 
注意这里的()表示的含义是函数调用(Function call),它也被认为是一种后缀表达式(Postfix Expression),这不同于我们在数学上常用的添加括号(Parentheses)来改变运算顺序的情况。添加括号来改变运算顺序的情况(Parenthesized Expression)是构造了一种初等表达式(Primary Expression)。将括号内的表达式看作是一个整体,其类型和值与无括号的表达式相同。从某种意义上讲,添加括号(Parenthesized Expression)的这种情况是比函数调用()更高的优先级。
其中,[],->同级,它们和()一样,属于最高优先级的那一类运算符;* ,&同级,它们属于第二档优先级的运算符。
对于->符号来说,它是一种对于结构体(struct)指针的简便写法,对于结构体指针p,p->str等价于(*p).str
由于我们有了运算符的优先级和结合顺序,因此也就可以由此读懂复杂的类型声明代码。其中,所有的类型声明语法都应该从里到外(按照优先级顺序)来读:
// 变量f是一个function,这个函数的返回值是一个pointer,这个pointer指向int,参数情况未知  | 
最后简单来谈一下函数指针和数组解引用的问题:
对于函数来说,函数的变量名func等价于这个函数定义时(func(params){定义起始位置....})的起始位置(address of function,这也就是操作系统中寄存器存储的return address),而一个函数的指针&func就是对这个函数的变量名进行取地址操作
对于数组来说,数组的变量名array等价于指向数组第一个元素的一个指针,而获得数组的第i个元素的值其实就是对数组指针进行解引用操作,array[i] = *(array + i),&array[i] = array + i。与++相同,对于指针型的变量,我们的+i操作实际上是+ i * sizeof(*array)