2.6 C语言的运算符
C语言有丰富的运算符,它把几乎所有的基本操作都作为运算符来处理。C语言的运算符有32个,这些运算符还可以灵活地与运算对象组合成相应的表达式。丰富的运算符使它的运算能力非常强。
下面我们分类来介绍C语言的运算符。
2.6.1 算术运算符
算术运算符及其作用如表2-4所示。在除法中,“/”两侧可以是整数,也可以是实数。如果两侧都是整数,则运算结果也将是整数,并自动将小数部分去掉。比如7/3=2,而不是7/3=2.33。如果“/”两侧除数或者被除数中有一个数是实数,则运算结果是double型的。因为所有的实数都是按double型进行计算的。
表2-4 算术运算符及其作用

在算术运算符中,乘法、除法、取模的优先级高于加法、减法。要改变这种运算次序,可以给表达式加上圆括号。当有多层括号时,应先计算最里层括号里的表达式。
算术运算符的结合性都是从左向右进行操作的。
2.6.2 关系运算符
关系运算符进行二目运算,用它们来比较两个相同类型数据的大小、相等或不相等。其运算结果是一个逻辑值。如关系成立,其值为真,用非0的值表示;如关系不成立,则其值为假,用0表示。
C语言中,对于逻辑值的真和假用整数1和0表示,也就是说,逻辑值真假和整数值1与0实质上是一回事。这样一来,逻辑运算其实可以转换为数值运算。
关系运算符及其作用如表2-5所示。在关系运算中,>、>=、<、<=的优先级相同,==和!=的优先级相同,并且>、>=、<、<=的优先级高于==和!=。比如x>y!=z,等效于(x>y)!=z,再比如y+z<x,等效于(y+z)<z。
表2-5 关系运算符及其作用

关系运算都是右结合的,其优先级低于算术运算,因此,如有关系表达式10+105<d-x,则应先进行10+105和d-x这两个算术运算,然后把它们的结果进行比较,从而求出关系表达式的值。
关系运算符的优先级又高于赋值运算符,有如下关系:
算术运算符>关系运算符>赋值运算符
2.6.3 逻辑运算符
逻辑与、逻辑或是二目运算符,它们对操作对象自左向右进行逻辑运算,而逻辑非则是单目运算符,它仅对运算符后面的数据进行逻辑取反运算,如表2-6所示。
表2-6 逻辑运算符

逻辑运算符两边的操作数不仅可以是0或1,也可以是非0的整数,还可以是任何数据,如实型、字符型,甚至是指针型数据。凡是非0就被认为是“真”,是0则被认为是“假”,如表2-7所示。
表2-7 逻辑真值表

2.6.4 自增自减运算符
如表2-8所示,++和--运算符被称为自增、自减运算符。比如p++、++p、p--、--p,p++和++p等价于p=p+1,p--和--p等价于p=p-1。p++和++p的区别在于,对P++先用p的值进行运算,然后把p的值加1,而对++p,则先把p的值加1,再用加1后的p的值进行运算。假设p=5,那么k=p++,则运算后k的值为5,而p的值为6。反过来,k=++p,运算后k的值为6,而p的值也是6。
表2-8 自增自减运算符

自增自减运算符的结合方向是自右向左,而算术运算符的结合方向是自左向右,但负号运算符结合方向是自右向左。如果有表达式-p++,由于-和++运算结合方向都是自右向左,所以上式相当于-(p++)。
另外,自增和自减运算符只能用于变量,而不能用于常量,也不能用于表达式。如--63和(a+b)++都是不允许的。
自增自减运算符是C语言中容易出现误解的一个运算符。例如:

上述表达式的值为多少?15?16?18?其实对于这种情况,C语言标准并没有作出规定。有些编译器计算出来为18,因为i经过3次自加后变为6,然后3个6相加得18;而有的编译器计算出来为16,比如Visual C++6.0,先计算前两个i的和,这时候i自加两次,2个i的和为10,然后再加上第三次自加的i得到16。
其实这些没有必要死记,用到哪个编译器写句代码测试一下就行了,但绝对不会计算出15的结果。
自增自减运算符使用妥当了,自然能提高效率,同时使程序简洁。
【例2.9】从键盘输入一串字符,然后倒序输出。


该段程序能够将输入的字符串倒序输出,比如输入“this is the first example”,则将这句话中的每个字母倒序输出。
2.6.5 赋值运算符
赋值运算符是将其右边表达式的值赋给左边的变量,赋值号左侧一定是变量,右侧一定是表达式。先计算,后赋值。比如y=a+b,则先求a+b的计算结果,然后把结果赋给y。
注意
C语言中表示相等用“==”,而不是“=”,这两者不能搞混淆了。
在进行赋值运算时,系统会对不同类型的数据自动进行类型转换。转换原则是:
(1)把实型数据赋值给整型变量时,小数部分会被丢掉。
(2)把一个long int型数据赋值给int型变量时,long型数据将被截取,其低16位赋给int型变量,而高16位则被丢掉。
在变量初始化说明中,不允许连续给多个变量赋值。例如:

是错误的,必须写成:

但是赋值语句则允许连续赋值(赋值语句必须用分号结尾),例如a=b=c=5作为语句则是合法的。例如:

还比如:

是合法的,但是:

则是错误的,因为“x=y+5;”是语句,不能出现在表达式中。
2.6.6 复合运算符
赋值运算符和其他运算符结合成复合运算符。常用的复合运算符有:+=、-+、*=、/=、%=、<<=、>>=、&=、|=、^=。
例如:
x%=3
x+=y
a*=9
b/=m
c-=32
y&=x
y>>=x
y<<=x
y^=x
对这些复合运算符应先进行运算再进行赋值,即:
x%=3相当于z=z%3
x+=y相当于x=x+y
a*=9相当于a=a*9
b/=m相当于b=b/m
c-=32相当于c=c-32
y&=x相当于y=y&x
y>>=x相当于y=y>>x
y<<=x相当于y=y<<x
y^=x相当于y=y^x
对于重合运算符来说,等号右边是一个整体,例如:
b/=s+t相当于b=b/(s+t),而不是b=b/s+t。
复合运算符使得表达式非常简练,这也是C语言的一个特色。
2.6.7 条件运算符
条件运算符由“?”和“:”号组成,是C语言中仅有的一个三目运算符,该运算符需要三个操作数,它能够实现简单的选择功能。条件运算符的形式为:

其中,表达式1必须是逻辑型,比如在“x?a:b”中,x是整型变量,若x==0,则条件表达式的值为b,否则,条件表达式的值为a。
表达式2和表达式3可以是任意类型,并且表达式1和表达式2的类型可以不同。整个表达式结果的类型为表达式2和表达式3中类型高的一个类型。比如“x>y?2:1.5”中,如果x>y不成立,则条件表达式的值为1.5;若x>y成立,则条件表达式的值应为2,但由于1.5是浮点类型,比整数类型高,因此应将2转换为浮点类型2.0。
条件表达式的执行顺序是:先求表达式1的值,如果表达式1的值为非0,整个条件表达式的值取表达式2的值,如果表达式1的值为0,则整个条件表达式的值取表达式3的值。例如:x>y?x:y,表示的意思是如果x>y成立,则整个表达式取值为x,否则取值为y。
条件表达式并不能取代所有的if…else语句,只有if…else语句中内嵌的语句为赋值语句且两个分支都给同一个变量赋值时才能代替if…else语句。下面的if…else语句就无法用条件表达式直接代替。

但是它可以用语句“printf("%d\n",a>b?a:b);”代替,将条件表达式a>b?a:b的值输出。
条件表达式可以嵌套,在同一个条件表达式中可以多次出现条件运算符,例如:

2.6.8 逗号运算符
逗号运算符,用它将两个表达式连接起来,例如“3+5,6+9”这样的式子。
逗号运算符连接起来起来的式子称为逗号表达式,其一般形式为:

逗号表达式的求解过程是依次求解表达式1,表达式2,表达式3…,表达式n,整个式子最终的值是“表达式n”的值。例如:

逗号表达式无非是将若干个表达式“串联”起来,在很多情况下,使用逗号表达式只是想从左往右顺序求到各个表达式的值,而并非一定得到整个逗号表达式的值,因此,它又称为“顺序求值运算符”。逗号表达式最常用于循环语句中。例如:

2.6.9 位运算符
C语言支持按位运算,就是能对二进制位从低位到高位对齐后进行运算操作,位运算的作用及运算规则如表2-9所示。
表2-9 位运算符的作用及运算规则

关于移位操作,我们在后面将详细讲解。
2.6.10 sizeof运算符
在C语言中,sizeof是个运算符,但是好多初学者会误以为它是个函数。它的作用就是检测某种数据类型的字节大小。这对于确定当下所用编译系统对不同类型的数据的宽度定义是非常有用的。
sizeof运算符的用法如下:

这是最常规的用法,在实际它还有其他的用法,举例说明如下。
1.获取已知类型的大小
【例2.10】获取已知类型的大小。

2.获取类型大小可根据类型名,也可根据变量名
【例2.11】对类型名或者变量名获取类型大小。

3.对变量名(非类型名),sizeof也可以不要圆括号
【例2.12】获取变量名的类型大小。

4.sizeof(数组变量)获取的是数组大小,而非维数
【例2.13】获取数组大小。
