C运算符优先级

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

 

()

圆括号

(表达式)/函数名(形参表)

 

.

成员选择(对象)

对象.成员名

 

->

成员选择(指针)

对象指针->成员名

 

2

-

负号运算符

-表达式

右到左

单目运算符

(类型)

强制类型转换

(数据类型)表达式

 

++

自增运算符

++变量名/变量名++

单目运算符

--

自减运算符

--变量名/变量名--

单目运算符

*

取值运算符

*指针变量

单目运算符

&

取地址运算符

&变量名

单目运算符

!

逻辑非运算符

!表达式

单目运算符

~

按位取反运算符

~表达式

单目运算符

sizeof

长度运算符

sizeof(表达式)

 

3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

双目运算符

%

余数(取模)

整型表达式/整型表达式

双目运算符

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

双目运算符

5

<<

左移

变量<<表达式

左到右

双目运算符

>>

右移

变量>>表达式

双目运算符

6

>

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

双目运算符

<

小于

表达式<表达式

双目运算符

<=

小于等于

表达式<=表达式

双目运算符

7

==

等于

表达式==表达式

左到右

双目运算符

!=

不等于

表达式!= 表达式

双目运算符

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

13

?:

条件运算符

表达式1? 表达式2: 表达式3

右到左

三目运算符

14

=

赋值运算符

变量=表达式

右到左

 

/=

除后赋值

变量/=表达式

 

*=

乘后赋值

变量*=表达式

 

%=

取模后赋值

变量%=表达式

 

+=

加后赋值

变量+=表达式

 

-=

减后赋值

变量-=表达式

 

<<=

左移后赋值

变量<<=表达式

 

>>=

右移后赋值

变量>>=表达式

 

&=

按位与后赋值

变量&=表达式

 

^=

按位异或后赋值

变量^=表达式

 

|=

按位或后赋值

变量|=表达式

 

15

,

逗号运算符

表达式,表达式,…

左到右

从左向右顺序运算

说明:

同一优先级的运算符,运算次序由结合方向所决定。

简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符

 


Table
0. 为什么要掌握优先级
1. 优先级
1.1 优先级图表
1.2 运算符实例
1.3 优先级顺口溜
2. 结合性
3. 参考资料
Link:http://blog.chinaunix.net/space. ... blog&id=2880933
写代码的时候,常会翻看的一个表就是“c语言运算符优先级表”。c的运算符优先级常常很让人头疼。其实,在大学里学习c的时候,老师告诉大家这个不用一定背下来,用的时候可以找书,或者加小括号就可以了。我听了,但是后来发现错了。很多人都听了,但不是每个人都发现这是错的。以至于有人觉得把优先级背下来是“没事闲的”(http://zhidao.baidu.com/question/155120432.html?an=0&si=1)。

0. 为什么要掌握优先级
想想这两个问题:
a. 读别人的代码,遇到优先级问题看不懂,怎么办?
b. 一堆的括号,美观吗?
本想贴一张画来装饰墙壁,却用了一堆纸来固定!
有人说代码写多了,自然就会了。这个是很宽泛的说法。看你写的代码的水准,有些东西可能你一直都接触不到,何谈熟练。有些东西一定要梳理,总结。

1. 优先级

1.1 优先级图表

C运算符优先级
优先级最高者不是真正意义上的运算符,包括:数组下标,函数调用,结构体成员选择符。
单目运算符的优先级次之。(! ~ ++ -- - (type) * & sizeof)
然后是双目运算符。双目运算符里, 算数运算符(* / % + -)优先级最高, 移位(<< >>)次之, 关系运算符(< <= > >= != ==)再次之, 接着是位运算符(& ^ | ),逻辑运算符(&& ||) 条件运算符(?: 三目),赋值运算符(= ...)。
任何一个逻辑运算符的优先级低于任何一个关系运算符。
移位运算符的优先级比算数运算符要低,但是比关系运算符要高。

1.2 运算符实例
a. while (c = getc(in) != EOF)
putc(c, out)
循环的意思是复制一个文件到另一个文件。但是由于!=的优先级比赋值运算符的优先级高,所以c被赋予了getc()的返回值与EOF比较后的布尔值,结果向out中写入了一堆1.
b. 解释下面几个声明

  1. char *p[];
  2. char (*p)[];
  3. int *fp();
  4. int (*fp)();
复制代码

char *p[]
常常被错误的理解为指向字符数组的指针。
正确的是p一个数组,里面元素是指向字符的指针类型。
char (*p)[]
p是指向指向字符数组的指针。
int *fp()
常常错误理解为函数指针,该函数返回int类型。
正确的是fp是一个函数,他返回一个执行int的指针。
int (*fp)()
fp是函数指针,该函数返回int类型。

c. 解释下面的表达式

  1. *p.f;
  2. val & mask != 0;
  3. max = val1 > val2 ? val1 : val2;
复制代码

*p.f
对p去f偏移,作为指针,然后进行解引用。相当与*(p.f),因为.的优先级高与*。比较(*p).f。
val & mask != 0
相当与val & (mask != 0).
max = val1 > val2 ? val1 : val2
相当与 max = (val1 > val2 ? val1 :val2).

d. 一个复杂的声明

  1. char *(* c[10])(int **p);
复制代码

1. 有两个小括号,小括号的结合行是自左向右,所以我们先关注第一个小括号,简化声明(*c[10])();
c是一个数组,里面放10指针,后面紧跟这一个括号,所以这些指针是函数指针。
2. 关注第二个括号,(int **p)
显然p是函数的参数,它是个指向指针的指针。
3. 这看最前面的*
char *说明该函数的返回值是一个指向字符的指针。
4. 这个声明的意思就是:c是一个数组,里面有10个函数指针,指向的函数返回指针,指向字符,函数的参数是指向int类型指针的指针。

1.3 优先级顺口溜
醋坛酸味灌
味落跳福豆

共44个运算符

醋-初等,4个: ( ) [ ] -> 指向结构体成员 . 结构体成员
坛-单目,9个: ! ~ ++ -- -负号 (类型) *指针 &取地址 sizeof长度
酸-算术,5个: * / % + -减
味-位移,2个: << >>
灌-关系,6个: < <= > >= == 等于 != 不等于
味-位逻,3个: & 按位与 ^ 按位异或 | 按位或
落-逻辑,2个: && 逻辑与 || 逻辑或
跳-条件,1个,三目: ? :
福-赋值,11个: = += -= *= /= %= >>= <<= &= ^= |=
豆-逗号,1个: ,

2. 结合性
在标准C语言的文档里,对操作符的结合性并没有作出非常清楚的解释。一个满分的回答是:它是仲裁者,在几个操作符具有相同的优先级时决定先执行哪一个。
看例子:

  1. int a, b = 1, c = 2;
  2. a = b = c;
复制代码

这个表达式只有赋值符,这样优先级就无法帮助我们呢决定哪个操作先执行。如果a = b先执行,然后 b = c执行。那么a最终取1。如果b = c先执行, a = b后执行,那么a最终取2。到底哪一个先执行?看结合性,复制的结合性是右至左,所以b = c,然后a = b。
同级的操作符,结合性相同。如果在计算表达式的值时候需要考虑结合性,那么最好把这个表达式一分为二。

3. 参考资料
《c Traps and Pitfalls》 Andrew Koenig著, ISBN 978-7-115-17179-5
《c 专家编程》 Peter Van Der Linden 著, ISBN 978-7-115-17180-1
顺口溜http://blog.sina.com.cn/s/blog_4e64e2290100be0z.html