第 3 讲:运算符
运算符是什么?
=
叫做赋值运算符,它被称为一个广义的运算符,因为它实际上并不称为一种运算,而是一种行为:从微观角度来看,它是把符号右侧的数据结果,放到变量 i
存放的位置上;从宏观来看,其实就是使得 i
得到 5 的这个结果的行为,就称为赋值(Assignment)。
实际上,不仅是加号,在 C 语言里,我们提供了很多执行操作,稍后我们将挨个写出来。
表达式是什么?
表达式(Expression),就是一个计算式子,这个式子拥有一个结果。从目前来看,它可以和 printf
、scanf
等语句来讲独立开来。表达式最终有一个数值结果,并且这个结果可以被其它地方所利用(当然你也可以不用它)。比如
2 + 3
就是一个表达式,而表达式的结果就是 5,我们利用了这个结果,赋值给了 i
实际上没有任何问题,表达式是可以单独成句的,但是这样一来,得到的 5 也没有任何意义。
运算符一览
数学运算符
第一个种类的运算符称为数学运算符(Math Operator),它用来表达一些数学运算期间产生的运算符号。我们暂且先不说 sin
等函数符号,我们在 C 语言里先暂时不当成运算符,而只关注于那些用非字母构成的符号,比如 +
、-
等等。
数学运算符在 C 语言里提供了如下一些:
正号(
+
)负号(
-
)加号(
+
)减号(
-
)乘号(
*
)除号(
/
)取模运算(
%
)
首先是正负号。正负号都只需要在符号右侧添加一个变量名称,或一个数值常量,表示这个数的本身,或这个数的相反数。例如
顺便提一下,这里的正负号都只需要为其旁边添加一个数字作为计算即可,所以这样的运算符称为一元运算符或单目运算符(Unary Operator)。
下面来说一下,需要两个数字参与计算的这五个基础的计算符号了:加减乘除模。这些符号称为二元运算符或双目运算符(Binary Operator),因为需要两个数字才能起作用。
加减乘三个运算符直接用就可以了。比如下面的例子:
那么这里就需要提及一点了。不同的数据类型运算是具有不同的精度的,比如小数就有精度一说,而整数没有。在 C 语言里,如果你运行好了程序,系统将会为你默认显示 6 个小数,所以输出的结果就是注释里给出的那个样子;当然,
double
类型还能为你显示 12 位小数,于是你就会看到一大堆的 0。如果你要控制小数位数的显示的话,请使用前文介绍的显示小数位数的格式化字符串来处理。比如%.2f
就表示小数点后只会两位。
当然,数据类型进行混用也是可以的。比如一个整数加一个小数,在 C 语言里是允许的,不过从微观来看,执行起来没有那么简单。
那么请你思考这个问题,result1
和 result2
分别都是多少呢?这里给出答案,result1
是 3,而 result2
是 3.5。实际上是很好理解的:由于计算的限制,一个整数和一个小数计算的时候,肯定会往“范围更大”的方向去处理。比如 int
类型显然不可能让它无限制的大,这样就使得数字一定有一个取值范围,而不是像人脑那样无限制的大。实际上,float
的范围比 int
的更大,所以为了保证计算结果能够放得下,结果一定是 类型来存储的,否则 int
类型就可能会出现存不下,进而出错的问题。
现在考虑到这个的时候,a
和 b
的求和结果肯定就是 float
类型所表示的 3.5 了。但你仔细观察代码,就可以发现,result1
可是 int
类型的,这意味着这个它想把 float
的结果用 int
表示出来,所以最终 3.5 被程序截断(注意,并不是四舍五入),于是这个数就变为了 3;同理,第二个则完全不需要转换,所以 result2
一定是 3.5 这个结果。
那么,这里就学习到了一个知识点,叫做类型转换(Type Conversion)。类型转换分两种,一种是人为强制定义这个结果转换为另一种类型的模式,叫做强制类型转换或显式转换(Explicit Conversion);而另外一种,就是上面这样,通过系统自己的机制进行的类型转换,叫做隐式转换(Implicit Conversion)。
但是,可以发现,不论类型较小转较大,还是较大转较小,C 语言貌似都给出了隐式转换的模式和手段。比如
float
转int
就是典型的大转小,而它采用的手段是截断(Truncate),而不是四舍五入(Round),所以在使用这种转换的时候应小心数据类型不同时引发的一些潜在的问题。
接下来是除法和求模运算。除法需要大量使用类型转换的知识点,所以需要你注意和小心每一步的执行逻辑了。除法和数学上有些不同,因为 C 语言有整数和小数的区分制度,所以除下来的结果不总是和数学相同的。
如果除号两边的数都是整数(int
、long
等),结果一定是整数;如果两边的数不都是整数的话(至少有一个是小数),结果都应为小数。
这句话什么意思呢?
可以看到,这里举出的例子都是一些比较奇特的使用方式,它展示出了所有除法需要注意的地方。
最后我们来看下求模运算。
取模运算即带余除法下的余数数值。但和数学不同的是,C 语言的取模运算的两边的数字,都必须是整数才可以进行计算。
如果除法下有一个负数值的话(注意负数也是整数),结果取决于被除数的符号
-
和减法 -
都是一样的。包括前面的运算符也是一样。
赋值运算符
接下来我们来看一些赋值运算符。
普通赋值(
=
)自增自减运算符(
++
和--
)复合赋值运算符(
op=
)
普通的赋值运算符在前文里其实已经说了,所以这里大可不必去讲。不过这里还需要说一种情况。
可以看到,这个等号在这里和数学意义就完全不同了。在编程里,=
表示把右侧的结果赋值给左边,即 a
变量的内存区域存放的是原始的数值再加 5 的新结果。所以从宏观来看,这样的写法就好比让变量自己增大 5 个单位。
当自增为 1 或自减为 1 的时候,我们可以使用自增自减的单目运算符,来操控变量增大或减少一个单位。比如
++
b
应该是多少呢?这就牵扯到了自增自减运算符的前缀和后缀写法的不同了。
自增自减运算符是为了将变量自身的数值增大或减小一个单位而特别定义出来的。因为大多数计算操作之中都会使用到这一点,所以C语言单独提供了这样的一种东西。
另外,自增自减运算符也可以写到变量前面。不过意义不同。现在来阐述一下。
所以这个题的结果为 4444
才对。你做对了吗?
我们知道,编程之中的等号和数学上的等号的意义不同,编程的等号实际是一个操作,将右侧的数据的数值结果拿到左侧变量的位置上存放。所以C语言之中,a = a + 1
这种写法才得以存在(将 a
原来的数值加 1,得到的新数值再重新放到 a
变量的存储位置上存放)。不过,这样的写法有些丑陋,因为确实等号两侧又有相同的变量名字,写法也太长,所以才出现了复合赋值运算符一说。
复合赋值运算符是说,省略右侧相同的变量名,并把右侧的运算符号放到等号左侧拼在一起写的特殊规范。比如:
前文的 op=
的 op
表示的是运算符的符号,这里可以替换为任何运算符,包括稍后讲到的其他一些运算符。
比较运算符
比较运算符是用作数值比较之用的,每一个表达式都会产生一个特定的数值结果表征判断结果。基本的比较运算符一共有以下一些:
>
(大于)、<
(小于)、>=
(大于等于)、<=
(小于等于)、==
(等于)、!=
(不等于)。
前两个就不用说了,表示大于和小于;中间两个分别是大于等于和小于等于,只是因为数学的符号无法直接在代码里面打出来,所以才用两个符号拼在一起表示的这样的含义。第5个是等号,判断两个数是否相同;最后一个则是不等号,注意不等号的写法是感叹号和等号拼接的意思。
这样赋值是没有问题的,刚才说过,右侧会得到一个特定的数值来表征判断结果。首先2是不大于3的,所以a > b
是错误的,在C语言之中,错误的结果会被转为数字0进行数据处理;正确的则会被转为1,所以c
的值为0,而d
的值为1。而后面的a != a
肯定也不对,所以e
的值为0。
逻辑运算符
逻辑运算用于处理高中学到的命题和逻辑那样的东西,它提供了三种逻辑表达:
&&
(且)、||
(或)、!
(非)
数学上的且、或、非的符号写法在代码里面不好输入,所以只能用上面的三种写法代替。它最后也是一个表达式,也会产生一个数值,表征最终的判断结果。和上面一样,对的则会为转为 1,错的则会转为 0。不过,如果是人为定下的数字来直接表示判断结果的话,非 0 的数值都会化为“正确的”,而 0 则会化为“错误的”。
首先,因为 6 直接作为逻辑判断的一边来充当条件的话,因为是非 0 数值,所以会被转化为 1 处理(6 表示“正确的”,作为数值处理后为 1),而 b
是 0,作为判断的另一边的话,所以应当表示“错误的”,作为数值处理即 0,所以且的关系是同真才真,有假必假。所以最终a && b
表达式应当是“错误的”,最后的值是 0,所以 c
为 0。
d
用的是或,同假才假,有真必真。所以 6 是“正确的”数值,所以整体表达式为真。故赋值时,表达式用 1 表示正确的。所以 d
为 1。
e
是非 a,而 a 表示正确的,所以非 a 是错误的,故 e
= 0。
特别注意
逻辑运算符有一个特征,比如且的关系,如果有假就假,所以表达式符号 &&
的两边,如果左侧已经为假,意味着整个表达式一定为假,那么右侧的结果是不会测算的。如果带有计算式子,右侧结果不会被处理和计算。
注意,++a
此时不会运算,即 a
不会自增1,原因是 b
为 0,已经意味着整个表达式错误,后者不会被计算。
同理,或 ||
也是一样。
最后这里作出一个总结:

位运算符
计算位运算符
位运算是将数据值变为 0 和 1 的二进制数处理的方式。一共提供了四种操作:
&
(且)、|
(或)、~
(非)、^
(异或)
它具有和逻辑运算差不多的性质,不过是对于二进制数位操作处理的。处理时,是对位处理。比如:
另外,这里的异或需要提一下,异或表示“要么……要么……”,选其中任意一个才可以,都选和都不选都不行,换句话说,都选或都不选(1 ^ 1 = 0 ^ 0 = 0),任选其一都即可(0 ^ 1 = 1 ^ 0 = 1)。
移位运算符
移位运算符是将数值用二进制处理后,所有数值结果全往左或往右移动的结果。写法有这两种:
>>
(右移)、<<
(左移)
结论:右移多少位表示将数据缩小 倍,左移多少位表示将数据扩大
倍。其中的
表示取整操作,舍弃小数部分。
最后,所有的位运算符号也具有复合赋值运算符。比如实际运用之中,有这样的用途:
语句表示不借助其他变量交换变量存储的数据值。
a = 2, b = 3
或
另外还有一种交换变量的方式是这样的:
也可以。甚至缩写: