C 语言运算符是说明特定操作的符号,它是构造 C 语言表达式的工具。C 语言的运算异常丰富,除了控制语句和输入输出以外的几乎所有的基本操作都为运算符处理。除了常见的三大类,算术运算符、关系运算符与逻辑运算符之外,还有一些用于完成特殊任务的运算符,比如位运算符。
C 语言中的运算符绝对是 C 语言学习和使用的一个难点,因为在2011版的标准中,C 语言的运算符的数量超过40个,甚至比关键字的数量还要多。
1、运算符及优先级
运算符多的好处之一是使用灵活方便,但这涉及到运算符之间的优先级问题。比如做四则运算时,有先乘除后加减的规定,但是 C 语言中的运算符已经远不止四则运算中的加减乘除了,还有其他很多运算符。当它们出现在同一个表达式中时先计算谁后计算谁呢?
C 语言中各运算符的优先级如下表:
优先级
运算符
名称或含义
使用形式
结合方向
说明
1
[]
数组下标
数组名[常量表达式]
左到右
()
圆括号
(表达式)/函数名(形参表)
.
成员选择(对象)
对象.成员名
->
成员选择(指针)
对象指针->成员名
++
后置自增运算符
++变量名
单目运算符
--
后置自减运算符
--变量名
单目运算符
2
-
负号运算符
-表达式
右到左
单目运算符
(类型)
强制类型转换
(数据类型)表达式
++
前置自增运算符
变量名++
单目运算符
--
前置自减运算符
变量名--
单目运算符
*
取值运算符
*指针变量
单目运算符
&
取地址运算符
&变量名
单目运算符
!
逻辑非运算符
!表达式
单目运算符
~
按位取反运算符
~表达式
单目运算符
sizeof
长度运算符
sizeof(表达式)
3
/
除
表达式/表达式
左到右
双目运算符
*
乘
表达式*表达式
双目运算符
%
余数(取模)
整型表达式/整型表达式
双目运算符
4
+
加
表达式+表达式
左到右
双目运算符
-
减
表达式-表达式
双目运算符
5
<
左移
变量<
左到右
双目运算符
>>
右移
变量>>表达式
双目运算符
6
>
大于
表达式>表达式
左到右
双目运算符
>=
大于等于
表达式>=表达式
双目运算符
小于
表达式
双目运算符
<=
小于等于
表达式<=表达式
双目运算符
7
==
等于
表达式==表达式
左到右
双目运算符
!=
不等于
表达式!= 表达式
双目运算符
8
&
按位与
表达式&表达式
左到右
双目运算符
9
^
按位异或
表达式^表达式
左到右
双目运算符
10
|
按位或
表达式|表达式
左到右
双目运算符
11
&&
逻辑与
表达式&&表达式
左到右
双目运算符
12
||
逻辑或
表达式||表达式
左到右
双目运算符
13
?:
条件运算符
表达式1? 表达式2: 表达式3
右到左
三目运算符
14
=
赋值运算符
变量=表达式
右到左
/=
除后赋值
变量/=表达式
*=
乘后赋值
变量*=表达式
%=
取模后赋值
变量%=表达式
+=
加后赋值
变量+=表达式
-=
减后赋值
变量-=表达式
<<=
左移后赋值
变量<<=表达式
>>=
右移后赋值
变量>>=表达式
&=
按位与后赋值
变量&=表达式
^=
按位异或后赋值
变量^=表达式
|=
按位或后赋值
变量|=表达式
15
,
逗号运算符
表达式,表达式,…
左到右
从左向右顺序运算
上表中可以总结出如下规律:
结合方向只有三个是从右往左,其余都是从左往右。
所有双目运算符中只有赋值运算符的结合方向是从右往左。
另外两个从右往左结合的运算符也很好记,因为它们很特殊:一个是单目运算符,一个是三目运算符。
C 语言中有且只有一个三目运算符。
逗号运算符的优先级最低,要记住。
此外要记住,对于优先级:算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符。逻辑运算符中 “逻辑非 !” 除外。
2、优先级的一些特别说明
上表中,优先级同为 1 的几种运算符如果同时出现,那怎么确定表达式的优先级呢?这是很多初学者迷糊的地方。例如我们常常对指向多维数组的指针和指针数组,还有指向函数的指针数组等含混不清,对初学者来说就更是迷惑不解。所以在这样的定义表达式中,如果优先级不明确结果有可能相去甚远。
下表就整理了这些容易出错的情况:
优先级问题
表达式
想要的结果
实际结果
.的优先级高于*
*p.f
指针p所指向的对象的某一字段f,即:(*p).f
对p取f偏移作为指针,然后进行解除饮用操作,即:*(p.f)
[]的优先级高于*
int *p[]
P是指向int数组的指针,即:int (*p)[]
P是元素为指向int的指针的数组,即:int *(p[])
函数()优先级高于*
int *p()
p是个函数指针所指函数返回值是int,即:int (*p)()
P是个函数,返回值是int *,即:int *(p())
==和!=优先级高于位操作
(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<
逗号运算符的优先级最低
i=1,2
i=(1,2)
(i=1),2
那么是不是有了上面的总结就稳妥了呢?当然不可能,且不说我们的总结很不完备,就算记住了所有的规则,在使用过程中仍然有可能漏洞百出,最好的办法是养成良好的编码习惯!
3、由优先级引发的错误
优先级问题经常会造成一些意想不到的结果,有的甚至非常严重,而且很难察觉到哪个地方运算符的优先级存在错误。当然,如果能够熟记各种运算符的优先级也许能够避免由运算符的优先级引发的问题,但实际上是很难达到的,C 语言的运算符运用非常灵活,编码的时候不可能时刻关注其优先级。
而且事实上在编程的时候也不需要考虑优先级的问题。因为如果不知道优先级高低的话,加一个括号就可以了,因为括号( )的优先级是最高的。
简而言之,首先,不需要专门记忆,也没有必要。因为作为初学者,哪个优先级高、哪个优先级低我们很难记住。就算死记硬背记住了,时间长不用也会忘记。所以当一个表达式中有多个运算符时,如果不知道哪个优先级高哪个优先级低就查一下优先级表。用的时间长了自然而然就记住了,这样记才会记得深刻。
其次,还是养成良好的编码习惯和多用多练更靠谱。一定要多调试,光靠看代码,水平是很难提上来的。
附C语言运算符优先级口诀:
括号成员第一; //括号运算符 []() 成员运算符 . ->
全体单目第二; //所有的单目运算符比如 ++、--、+(正)、-(负)、指针运算*、&乘除余三,加减四; 这个"余"是指取余运算即 %
移位五,关系六; //移位运算符:<< >>,关系:> < >= <= 等
等于(与)不等排第七; //即 == 和 !=
位与异或和位或; //这几个都是位运算: 位与 & 、异或 ^ 、位或 |
"三分天下"八九十;
逻辑或跟与; //逻辑运算符:|| 和 &&
十二和十一; //注意顺序:优先级(||) 低于 优先级(&&)
条件高于赋值; //三目运算符优先级排到 13 位只比赋值运算符和","高
逗号运算级最低; //逗号运算符优先级最低
欢迎关注微信公众号『OpenSSR』