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运算符优先级及使用问题的更多相关文章

  1. 重读The C programming Lanuage 笔记二:运算符优先级

    运算符的优先级和结合性有明确的规定,但是,除少数例外情况外,表达式的求值次序没有定义,甚至某些有副作用的子表达式也没有定义. 也就是说运算符的定义保证了其操作数按某一特定的顺序求值,否则具体实现可以自 ...

  2. C语言学习及应用笔记之七:C语言中的回调函数及使用方式

    我们在使用C语言实现相对复杂的软件开发时,经常会碰到使用回调函数的问题.但是回调函数的理解和使用却不是一件简单的事,在本篇我们根据我们个人的理解和应用经验对回调函数做简要的分析. 1.什么是回调函数 ...

  3. C语言学习及应用笔记之五:C语言typedef关键字及其使用

    在C语言中有一个typedef关键字,其用来定义用户自定义类型.当然,并不是真的创造了一种数据类型,而是给已有的或者符合型的以及复杂的数据类型取一个我们自己更容易理解的别名.总之,可以使用typede ...

  4. C语言学习及应用笔记之三:C语言const关键字及其使用

    在C语言程序中,const关键字也是经常会用到的一个关键字,那么使用const关键字的目的是什么呢?事实上,在程序中使用const关键字的主要目的就是为了向使用者传递设计者的一些意图. 事实上,无论我 ...

  5. C语言学习及应用笔记之二:C语言static关键字及其使用

    C语言有很多关键字,大多关键字使用起来是很明确的,但有一些关键字却要相对复杂一些.我们这里要说明的static关键字就是如此,它的功能很强大,相应的使用也就更复杂. 一般来说static关键字的常见用 ...

  6. C语言学习及应用笔记之六:C语言extern关键字及其使用

    在C语言中,修饰符extern用在变量或者函数的声明前,用来以标识变量或者函数的定义在别的文件中,提示编译器遇到此变量或者函数时,在其它文件中寻找其定义.extern关键字的用法有几种,我们下面对其进 ...

  7. C语言学习及应用笔记之四:C语言volatile关键字及其使用

    在C语言中,还有一个并不经常使用但却非常有用的关键字volatile.那么使用volatile关键字究竟能干什么呢?接下来我将就此问题进行讨论. 一个使用volatile关键字定义变量,其实就是告诉编 ...

  8. HTML语言学习笔记(会更新)

    # HTML语言学习笔记(会更新) 一个html文件是由一系列的元素和标签组成的. 标签: 1.<html></html> 表示该文件为超文本标记语言(HTML)编写的.成对出 ...

  9. 郝斌老师C语言学习笔记(一)

    在给变量分配内存时,很可能这段内存存在以前其他程序使用留下的值.当使用VC编译器,若编译器发现没有给变量赋值而使用,就会返回一个以“85”开头的很大的数字(此时该段内存中为一个垃圾数,为了避免出现较常 ...

随机推荐

  1. 如何使用xss带cookie

    参考连接:https://pentesterlab.com/exercises/xss_and_mysql_file/course 打开测试网站: 然后在192.168.1.46机器上使用socat, ...

  2. Unet网络

    近期利用遥感影像进行路网提取,利用Unet网络进行图像分割 介绍如下: U-net网络非常简单,前半部分作用是特征提取,后半部分是上采样.在一些文献中也把这样的结构叫做编码器-解码器结构.由于此网络整 ...

  3. 第26月第22天 iOS瘦身之armv7 armv7s arm64选用 iOS crash

    1.iOS瘦身之armv7 armv7s arm64选用 机器对指令集的支持是向下兼容的,因此armv7的指令集是可以运行在iphone5S以上的,只是效率没那么高而已~ 但是由于苹果要求必须支持ar ...

  4. vue框架导入百度地图API接口的方法

    百度请求API接口:

  5. git关联了无用的,取消关联,并重置gitignore

    一定要在首次上传git之前配置好 ignore文件假如操作问题不小心点了studio中可视化界面的git add . -f (强制的关联所有)这时候如果你看小git status 所有的文件都进行了远 ...

  6. Callable的用法示例

    //Runnable是执行工作的独立任务,但是它不返回任何值.在JavaSE5中引入的Callable是一种具有类型参数的泛型,它的类型参数表的是从方法call()中返回的值,并且必须使用Execut ...

  7. Gulp简明使用教程

    Glup用自动化构建工具增强你的工作流程! 同类型的软件还有Grunt.关于两者的区别可以参考这篇文章Grunt VS Gulp 安装: $ npm install gulp -g $ npm ins ...

  8. 使用Numpy将数据集中的data和target同时shuffle

    假设现在有图像数据imgs和对应标签targets.数据维度分别如下 imgs.shape = (num, channel, width, height) targets.shape = (num, ...

  9. JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m

    https://blog.csdn.net/u012882327/article/details/69525166

  10. 通过SecureCRT连接虚拟机

    继续上一篇: http://www.cnblogs.com/CoolJayson/p/7430421.html 上一篇配置了虚拟机网络环境, 实际开发中通常使用SecureCRT或Xshell等连接L ...