Sud0x67
一个写代码的俗人.

深入理解c语言运算符优先级

2021-07-10 c语言 运算符
Word count: 2.1k | Reading time: 7min

深入了解c语言运算符优先级

引言

很多刚学编程的同学对c语言运算符的优先级往往存在一些困惑,对于一些已经入门了的同学一些不太常见的用法也较难理解,比如常见的函数指针:

1
2
3
4
5
6
7
8
//函数指针
int (*add)(int a , int b);
//返回指针的函数
int * getAddress(char [] , int n);
//数组指针
int (*a)[]
//指针数组
int * a[];

刚刚接触这些的时候我们往往很难理解这些符号,即使我们理解了也往往对于新的符号也经常出现困惑:

1
*p++;(*p)++;*++p;++*p;*p.f

而彻底理解C语言运算符的优先级,能让你对这些游刃有余,避免在编程的时候出现困惑,下面我们就一起来探究一下C运算符的优先级吧!

运算符的四要素——符号、操作数、优先级、结合性

符号

符号很容易理解,我们平常所见的+-*/等都是符号,运算符符号指代表某一种运算的符号。划重点:运算符一定是代表了某一种运算,例如:¥虽然也是符号就不是C语言的运算符。

操作数(operand)

操作数是指需要进行运算的数字或者表达式,如1+1,有两个操作数分别是1,和1;再例如 a* b + c / d 这个表达式中+号的两个操作数分别是(a* b)和(c/d)两个表达式的值,操作数不光是数字常量、字符常量也包括可以赋值的各种变量。这些操作数就像是函数的参数,根据“参数”个数的不同运算符分为一元运算符、二元运算符、三元运算符等。

优先级

优先级的概念很好理解,我们小学数学学的先乘除再加减就是一种优先级。给出任意一个表达式例如3+2*y[i]++;因为优先级[] = ++ > * >+ ,我们根据各项运算符优先级不难理解表达式所要表达的含义。

结合性

运算符想要进行运算需要有操作数,结合性顾名思义是操作符与操作数结合的亲密程度。例如前面的例子中++ 和[]拥有相同的优先级但是明显[]与y更加紧密([]距离y更近),所以我们知道是对数组中第i+1个元素进行++,而不是y进行++(这也不和语法)。C语言中运算符的结合性包括从左到右和从右到左两种。从左到右:即优先级相同计算时先算左边的后算右边的;从右到左:即运算符优先级相同先计算左边的在计算右边的。
c语言中运算符的结合性通常都为从左到右,只有前缀一元运算符:* 、&、++(前缀)、–(前缀)、-(负号)、+(正号)、!、~(按位取反)(typename)强制转换、sizeof 、各种赋值运算符、三元运算符:? :这三类为从右到左。

各种运算符的优先级与结合性

下表列出了C语言中运算符的结合性和优先级,可以看出我们小括号()和{}(数组或指针初始化时使用)虽然不算是严格意义上的运算符,但()和{}中的内容通常被认为一个整体(这和数学上的括号意义一致)。所以如果实在确定不了优先级,可以使用() 例如if(a==b&&c==d||d==e&&e==f)等价于if((a==b&&c==d)||(d==e&&e==f)),添加()也使得我们的小程序可读性更高。
除了()和{}之外优先级依次为:一元后缀运算符>一元前缀运算符>算术运算符( * / %)> 算术运算符(+- )>位运算符(>>、<<)>关系运算符(>=、>、<、<=)>关系运算符(==、!=)> 位逻辑运算符(依次&、^、|)>逻辑运算符(依次&&、||)>三元运算符(?:)>赋值运算符>逗号运算符(,)。
简单的来说,优先级顺序满足以下几项:

  • 一元后缀运算符>一元前缀运算符>二元运算符>三元运算符
  • 二元运算符中:算术运算符>关系运算符>逻辑运算符>赋值运算符
  • 逻辑运算符: && > ||

结合性比较简单:除了一元前缀运算符、三元运算符和赋值运算符为从右到左以外,其余均为从左到右。

运算符优先级 运算符 结合性 备注
0 ()(括号)、{}(组合文字) 括号和数学运算的括号一致只是声明内部为一个整体,组合文字用了表示数组或结构直接量,严格讲这两个并不算是运算符
1 ()(函数调用)、++(后缀)、- -(后缀)、[](数组下标). 、-> 从左到右 ()和[]在函数和数组声明时也适用此优先级
2 ++(前缀)、- -(前缀)、*(取值)、&(取指针)、+(正号)、-(符号)、!(取反)、~(按位取反)、sizeof 、 (typename)(强制转换)、 从右到左 * 在指针声明时也适用此优先级
3 * 、/ 、%、 从左到右 算术运算符
4 +、- 从左到右 算术运算符
5 <<、>> 从左到右 位运算符-移位运算符
6 <=、<、>、>= 从左到右 关系运算符
7 ==、!= 从左到右 关系运算符
8 & 从左到右 位逻辑运算符
9 ^ 从左到右 位逻辑运算符
10 | 从左到右 位逻辑运算符
11 && 从左到右 逻辑运算符
12 || 从左到右 逻辑运算符
13 ?: 从右到左 三元运算符
14 =、+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|= 从右到左 赋值运算符
15 ,(逗号运算符) 从左到右 逗号运算符

补充

关于位运算符

位运算的优先级一直饱受争议,人们认为|、&、^应该与移位运算符(<</>>)一样,例如a&b==c我们往往期待它解析为(a&b)==c,然而事实是它被解析为a&(b==c)。这与我们期望并不相符。主要原因是历史上c并没有&&和||只有&和|,所以位运算&和|被认为是逻辑运算符。

关于条件表达式(?:)

通常优先级和结合性可以解决我们遇到的大部分问题。但有时候运算符本身的含义规定了另一些东西。例如a>b?c,d:e被解释为a>b?(c,d):e,因为(a>b?c),(d:e)这样会毫无意义。同样sizeof (int) *x被解释为(sizeof(int))*x,并非sizeof((int)*x),一般sizeof的操作数最好加上()表示。尽管sizeof n也是合法。
C语言中关于条件表达式的规则为:

logical-OR-expression ? expression : expression

所以对于条件表达式最好按照它的意义去理解,另外条件表达式结合性是从右到左a>b ? a : c>b ? c : b这个表达式被解释为a>b ? a : (c>b ? c : b)

对于一些运算符使用特殊情况的解释

1
2
3
4
5
6
7
8
//函数指针
int (*add)(int a , int b); //括号中*add被看做一个整体,*表明add是一个指针,而int (.)()表明指针指向的是函数类型。
//返回指针的函数
int * getAddress(char [] , int n);//*getAddress(*) ,*和()两个运算符同时与getAddress结合,而优先级()>*,表明getAddress是一个函数名。
//数组指针
int (*a)[];//括号中*a被看做一个整体,*表明a是一个指针,而int (.)[]表明指针a指向的是数组类型。
//指针数组
int * a[];//*a[],*和[]同时与a结合,而优先级[]>*表明a是一个数组,数组存储的内容为int类型的指针

总结

C语言中运算符优先级是C语法中很重要的一部分,Java、C++、Python、C#中运算符的优先级与C也大多相似。只要理解之后记住这些并不算难,但在实际应用中应当注意在适当的时候使用(),即使你很确定你的表达式没有必要加(),()可以保证我们的代码更加友好、可读。

Author: Sud0x67@github

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
搭建fabric环境时遇到的一些问题
NextPost >
mysql 8.0 安装时无设置密码界面
CATALOG
  1. 1. 深入了解c语言运算符优先级
    1. 1.1. 引言
    2. 1.2. 运算符的四要素——符号、操作数、优先级、结合性
      1. 1.2.0.1. 符号
      2. 1.2.0.2. 操作数(operand)
      3. 1.2.0.3. 优先级
      4. 1.2.0.4. 结合性
  2. 1.3. 各种运算符的优先级与结合性
  3. 1.4. 补充
    1. 1.4.0.1. 关于位运算符
    2. 1.4.0.2. 关于条件表达式(?:)
    3. 1.4.0.3. 对于一些运算符使用特殊情况的解释
  • 2. 总结