C语言学习及应用笔记之一:C运算符优先级及使用问题
C语言中的运算符绝对是C语言学习和使用的一个难点,因为在2011版的标准中,C语言的运算符的数量超过40个,甚至比关键字的数量还要多。这些运算符有单目运算符、双目运算符以及三目运算符,又涉及到左结合和右结合的问题,真是令人眼花缭乱。
1、运算符及优先级
运算符多可能使用更灵活方便,但这还涉及到运算符之间的优先级问题。我们做四则运算式时,有先乘除后加减的规定,在C语言的这些运算符中自然也是有的,但40多个运算符排起优先级来,使用就不那么容易了。
接下来我们简单的总结一下C语言中运算符以及他们的优先级。C语言中各运算符的优先级如下表所示:
优先级 |
运算符 |
名称或含义 |
使用形式 |
结合方向 |
说明 |
1 |
[] |
数组下标 |
数组名[常量表达式] |
左到右 |
|
() |
圆括号 |
(表达式)/函数名(形参表) |
|||
. |
成员选择(对象) |
对象.成员名 |
|||
-> |
成员选择(指针) |
对象指针->成员名 |
|||
++ |
后置自增运算符 |
++变量名 |
单目运算符 |
||
-- |
后置自减运算符 |
--变量名 |
单目运算符 |
||
2 |
- |
负号运算符 |
-表达式 |
右到左 |
单目运算符 |
(类型) |
强制类型转换 |
(数据类型)表达式 |
|||
++ |
前置自增运算符 |
变量名++ |
单目运算符 |
||
-- |
前置自减运算符 |
变量名-- |
单目运算符 |
||
* |
取值运算符 |
*指针变量 |
单目运算符 |
||
& |
取地址运算符 |
&变量名 |
单目运算符 |
||
! |
逻辑非运算符 |
!表达式 |
单目运算符 |
||
~ |
按位取反运算符 |
~表达式 |
单目运算符 |
||
sizeof |
长度运算符 |
sizeof(表达式) |
|||
3 |
/ |
除 |
表达式/表达式 |
左到右 |
双目运算符 |
* |
乘 |
表达式*表达式 |
双目运算符 |
||
% |
余数(取模) |
整型表达式/整型表达式 |
双目运算符 |
||
4 |
+ |
加 |
表达式+表达式 |
左到右 |
双目运算符 |
- |
减 |
表达式-表达式 |
双目运算符 |
||
5 |
<< |
左移 |
变量<<表达式 |
左到右 |
双目运算符 |
>> |
右移 |
变量>>表达式 |
双目运算符 |
||
6 |
> |
大于 |
表达式>表达式 |
左到右 |
双目运算符 |
>= |
大于等于 |
表达式>=表达式 |
双目运算符 |
||
< |
小于 |
表达式<表达式 |
双目运算符 |
||
<= |
小于等于 |
表达式<=表达式 |
双目运算符 |
||
7 |
== |
等于 |
表达式==表达式 |
左到右 |
双目运算符 |
!= |
不等于 |
表达式!= 表达式 |
双目运算符 |
||
8 |
& |
按位与 |
表达式&表达式 |
左到右 |
双目运算符 |
9 |
^ |
按位异或 |
表达式^表达式 |
左到右 |
双目运算符 |
10 |
| |
按位或 |
表达式|表达式 |
左到右 |
双目运算符 |
11 |
&& |
逻辑与 |
表达式&&表达式 |
左到右 |
双目运算符 |
12 |
|| |
逻辑或 |
表达式||表达式 |
左到右 |
双目运算符 |
13 |
?: |
条件运算符 |
表达式1? 表达式2: 表达式3 |
右到左 |
三目运算符 |
14 |
= |
赋值运算符 |
变量=表达式 |
右到左 |
|
/= |
除后赋值 |
变量/=表达式 |
|||
*= |
乘后赋值 |
变量*=表达式 |
|||
%= |
取模后赋值 |
变量%=表达式 |
|||
+= |
加后赋值 |
变量+=表达式 |
|||
-= |
减后赋值 |
变量-=表达式 |
|||
<<= |
左移后赋值 |
变量<<=表达式 |
|||
>>= |
右移后赋值 |
变量>>=表达式 |
|||
&= |
按位与后赋值 |
变量&=表达式 |
|||
^= |
按位异或后赋值 |
变量^=表达式 |
|||
|= |
按位或后赋值 |
变量|=表达式 |
|||
15 |
, |
逗号运算符 |
表达式,表达式,… |
左到右 |
从左向右顺序运算 |
在上表中,我们归纳了运算符、个运算符的功能、通常的应用表达式形式以及结合性。说到结合性主要应用于相同优先级的运算符,运算次序由结合方向所决定。绝大部分的运算符都是左结合的,与我们的常识一致。不过有一部分运算符是右结合的,这些就需要记忆了,但记忆有时候却不见得百分百准确,所以我们可以在编写代码时以( )加以规范。
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<<(4+lsb) |
逗号运算符的优先级最低 |
i=1,2 |
i=(1,2) |
(i=1),2 |
那么是不是有了上面的总结就完事大吉了呢?当然不可能,且不说我们的总结很不完备,就算你记住了所有的规则,在使用过程中仍然有可能漏洞百出,最好的办法是养成良好的编码习惯。
3、由优先级引发的错误
优先级问题经常会造成一些意想不到的结果,有的甚至是非常严重的。本人在编写程序时,就出现过一些因为疏忽运算符优先级造成的问题,而且检查起来非常不容易发现。
例如,有一次,我们采集了传感器的原始数据,然后会对数据进行一些处理,在其中的一种条件下会对一个数进行左移几位并加上一个数。由于算术运算符的优先级大于移位运算符,而程序员忘记了给移位操作加括号,所以得出了结果总是有误,只好从头开始查找,花了不少时间才发现这出错误。如msb<<4+lsb和(msb<<4)+lsb是完全不一样,因为算术运算符的优先级大于移位运算符。
还有一次,我们定义一个指向函数的指针数组用于回调函数的操作。定义时,没有考虑到指针运算符、函数运算符以及数组运算符的优先级问题而造成调用出错。如,void (*p[])f()的形式,如果写成void *p[]f()的形式就完全错误了。
当然,如果能够熟记各种运算符的优先级也许能解决上面的问题,但这实际上却很难达到,因为应用是非常灵活的,你不可能时时刻刻只关注于此。我觉得养成良好的编码习惯以及多多练习似乎更有效。
欢迎关注:
C语言学习及应用笔记之一:C运算符优先级及使用问题的更多相关文章
- 重读The C programming Lanuage 笔记二:运算符优先级
运算符的优先级和结合性有明确的规定,但是,除少数例外情况外,表达式的求值次序没有定义,甚至某些有副作用的子表达式也没有定义. 也就是说运算符的定义保证了其操作数按某一特定的顺序求值,否则具体实现可以自 ...
- C语言学习及应用笔记之七:C语言中的回调函数及使用方式
我们在使用C语言实现相对复杂的软件开发时,经常会碰到使用回调函数的问题.但是回调函数的理解和使用却不是一件简单的事,在本篇我们根据我们个人的理解和应用经验对回调函数做简要的分析. 1.什么是回调函数 ...
- C语言学习及应用笔记之五:C语言typedef关键字及其使用
在C语言中有一个typedef关键字,其用来定义用户自定义类型.当然,并不是真的创造了一种数据类型,而是给已有的或者符合型的以及复杂的数据类型取一个我们自己更容易理解的别名.总之,可以使用typede ...
- C语言学习及应用笔记之三:C语言const关键字及其使用
在C语言程序中,const关键字也是经常会用到的一个关键字,那么使用const关键字的目的是什么呢?事实上,在程序中使用const关键字的主要目的就是为了向使用者传递设计者的一些意图. 事实上,无论我 ...
- C语言学习及应用笔记之二:C语言static关键字及其使用
C语言有很多关键字,大多关键字使用起来是很明确的,但有一些关键字却要相对复杂一些.我们这里要说明的static关键字就是如此,它的功能很强大,相应的使用也就更复杂. 一般来说static关键字的常见用 ...
- C语言学习及应用笔记之六:C语言extern关键字及其使用
在C语言中,修饰符extern用在变量或者函数的声明前,用来以标识变量或者函数的定义在别的文件中,提示编译器遇到此变量或者函数时,在其它文件中寻找其定义.extern关键字的用法有几种,我们下面对其进 ...
- C语言学习及应用笔记之四:C语言volatile关键字及其使用
在C语言中,还有一个并不经常使用但却非常有用的关键字volatile.那么使用volatile关键字究竟能干什么呢?接下来我将就此问题进行讨论. 一个使用volatile关键字定义变量,其实就是告诉编 ...
- HTML语言学习笔记(会更新)
# HTML语言学习笔记(会更新) 一个html文件是由一系列的元素和标签组成的. 标签: 1.<html></html> 表示该文件为超文本标记语言(HTML)编写的.成对出 ...
- 郝斌老师C语言学习笔记(一)
在给变量分配内存时,很可能这段内存存在以前其他程序使用留下的值.当使用VC编译器,若编译器发现没有给变量赋值而使用,就会返回一个以“85”开头的很大的数字(此时该段内存中为一个垃圾数,为了避免出现较常 ...
随机推荐
- docker 容器内启动 sshd 启动报错
创建容器设置密码 安装 openssh-server 启动出错 在容器内 使用 /usr/sbin/sshd -d 启动报错? [root@9d41c7f36c5e tmp]# /usr/sbin/s ...
- [C++]2-6 排列
/* 排列(Permutation) 用1,2,3,...,9组成3个三位数abc,def和ghi,每个数字恰好使用一次,要求abc:def:ghi = 1:2:3. 按照"abc def ...
- Coursera, Deep Learning 5, Sequence Models, week3, Sequence models & Attention mechanism
Sequence to Sequence models basic sequence-to-sequence model: basic image-to-sequence or called imag ...
- python序列化
一. 序列化 1 定义: 在我们存储数据或者⽹网络传输数据的时候. 需要对我们的对象进⾏行行处理理. 把对象处理理成 ⽅方便便存储和传输的数据格式. 这个过程叫序列列化. 不同的序列列化, 结果也不同 ...
- C++ 类使用多线程技术
参考文章 : http://blog.csdn.net/jmh1996/article/details/72235232 成员函数作为线程函数, 要将成员函数定义为静态的 C++ 静态成员函数调用非 ...
- MySQL去重案列(待更新...)
谈谈distinct 查询单个字段,没问题! SELECT DISTINCT username FROM t_user 但是我想加入id字段,这样写,报错! SELECT id, DISTINCT u ...
- rpmlib(PayloadIsLzma) <= 4.4.6-1 is needed【转载】
以下为转载,但是有改动,原作者在一处写错了,将高写成了低,直接差之毫厘,谬之千里. 环境: centos el5 背景: 由于个人比较喜欢用软件的最新版本,在重新安装服务器上的 xdg-open(还有 ...
- 20165234 《Java程序设计》第一周学习总结
第一周学习总结 教材学习内容总结 java的特点 语法简单,面向对象,稳定,与平台无关,多线程,动态. 平台是由操作系统和处理器(CPU)所构成,每个平台都会形成自己独特的机器指令,相同的CPU和不同 ...
- Struts2学习(三)
一.值栈 1.OGNL表达式 OGNL的概述:对象图导航语言,是一门功能强大的表达式语言. 2.值栈 值栈的概述(ValueStack):是一个接口,实现类OgnlValueStack.是数据的中转站 ...
- Samples topic
Rendering: http://www.cnblogs.com/miloyip/archive/2010/03/29/1698953.html http://www.scratchapixel.c ...