算术运算符
算术运算符专门用于算术运算,主要有下面几种。
+:正值运算符(一元运算符)-:负值运算符(一元运算符)+:加法运算符(二元运算符)-:减法运算符(二元运算符)*:乘法运算符/:除法运算符%:余值运算符
+,-
+和-既可以作为一元运算符,也可以作为二元运算符。所谓“一元运算符”,指的是只需要一个运算数就可以执行。一元运算符-用来改变一个值的正负号。
int x = -12;
上面示例中,-将12这个值变成-12。
一元运算符+对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。
int x = -12;
int y = +x;
上面示例中,变量y的值还是-12,因为+不会改变正负值。
二元运算符+和-用来完成加法和减法。
int x = 4 + 22;
int y = 61 - 23;
*
运算符*用来完成乘法。
int num = 5;
printf("%i\n", num * num); // 输出 25
/
运算符/用来完成除法。注意,两个整数相除,得到还是一个整数。
float x = 6 / 4;
printf("%f\n", x); // 输出 1.000000
上面示例中,尽管变量x的类型是float(浮点数),但是6 / 4得到的结果是1.0,而不是1.5。原因就在于 C 语言里面的整数除法是整除,只会返回整数部分,丢弃小数部分。
如果希望得到浮点数的结果,两个运算数必须至少有一个浮点数,这时 C 语言就会进行浮点数除法。
float x = 6.0 / 4; // 或者写成 6 / 4.0
printf("%f\n", x); // 输出 1.500000
%
运算符%表示求模运算,即返回两个整数相除的余值。这个运算符只能用于整数,不能用于浮点数。
int x = 6 % 4; // 2
负数求模的规则是,结果的正负号由第一个运算数的正负号决定。
11 % -5 // 1
-11 % -5 // -1
-11 % 5 // -1
赋值运算的简写形式
如果变量对自身的值进行算术运算,C 语言提供了简写形式,允许将赋值运算符和算术运算符结合成一个运算符。
+=-=*=/=%=
下面是一些例子。
i += 3; // 等同于 i = i + 3
i -= 8; // 等同于 i = i - 8
i *= 9; // 等同于 i = i * 9
i /= 2; // 等同于 i = i / 2
i %= 5; // 等同于 i = i % 5
自增运算符,自减运算符
C 语言提供两个运算符,对变量自身进行+ 1和- 1的操作。
++:自增运算符--:自减运算符
i++; // 等同于 i = i + 1
i--; // 等同于 i = i - 1
这两个运算符放在变量的前面或后面,结果是不一样的。++var和--var是先执行自增或自减操作,再返回操作后var的值;var++和var--则是先返回操作前var的值,再执行自增或自减操作。
int i = 42;
int j;
j = (i++ + 10);
// i: 43
// j: 52
j = (++i + 10)
// i: 44
// j: 54
关系运算符
C 语言用于比较的表达式,称为“关系表达式”(relational expression),里面使用的运算符就称为“关系运算符”(relational operator),主要有下面6个。
>大于运算符<小于运算符>=大于等于运算符<=小于等于运算符==相等运算符!=不相等运算符
下面是一些例子。
a == b;
a != b;
a < b;
a > b;
a <= b;
a >= b;
关系表达式通常返回0或1,表示真伪。C 语言中,0表示伪,所有非零值表示真。比如,20 > 12返回1,12 > 20返回0。
注意==相等运算符和=赋值运算符不是一个东西
逻辑运算符
逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,主要有下面三个运算符。
!:否运算符(改变单个表达式的真伪)。&&:与运算符(两侧的表达式都为真,则为真,否则为伪)。||:或运算符(两侧至少有一个表达式为真,则为真,否则为伪)。
与运算符
if (x < 10 && y > 20)
printf("Doing something!\n");
上面示例中,只有x < 10和y > 20同时为真,x < 10 && y > 20才会为真。
或运算符
if (x < 10 || y > 20)
printf("Doing something!\n");
上面示例中,只要有一个是真,就会执行printf函数。
否运算符
if (!(x < 12))
printf("x is not less than 12\n");
上面示例中,由于否运算符!具有比<更高的优先级,所以必须使用括号,才能对表达式x < 12进行否运算。当然,合理的写法是if (x >= 12),这里只是为了举例。
位运算符
C 语言提供一些位运算符,用来操作二进制位(bit)。
取反运算符~
取反运算符~是一个一元运算符,用来将每一个二进制位变成相反值,即0变成1,1变成0。
// 返回 01101100
~ 10010011
注意,~运算符不会改变变量的值,只是返回一个新的值。
与运算符&
与运算符&将两个值的每一个二进制位进行比较,返回一个新的值。当两个二进制位都为1,就返回1,否则返回0。
// 返回 00010001
10010011 & 00111101
与运算符&可以与赋值运算符=结合,简写成&=。
int val = 3;
val = val & 0377;
// 简写成
val &= 0377;
或运算符|
或运算符|将两个值的每一个二进制位进行比较,返回一个新的值。两个二进制位只要有一个为1(包含两个都为1的情况),就返回1,否则返回0。
// 返回 10111111
10010011 | 00111101
或运算符|可以与赋值运算符=结合,简写成|=。
int val = 3;
val = val | 0377;
// 简写为
val |= 0377;
异或运算符^
异或运算符^将两个值的每一个二进制位进行比较,返回一个新的值。两个二进制位有且仅有一个为1,就返回1,否则返回0。
// 返回 10101110
10010011 ^ 00111101
异或运算符^可以与赋值运算符=结合,简写成^=。
int val = 3;
val = val ^ 0377;
// 简写为
val ^= 0377;
左移运算符<<
左移运算符<<将左侧运算数的每一位,向左移动指定的位数,尾部空出来的位置使用0填充。相当于二进制乘2的位数的指数
10001010 << 2
// 1000101000
上面示例中,10001010的每一个二进制位,都向左侧移动了两位。
左移运算符<<可以与赋值运算符=结合,简写成<<=。
int val = 1;
val = val << 2;
// 简写为
val <<= 2;
右移运算符>>
右移运算符>>将左侧运算数的每一位,向右移动指定的位数,尾部无法容纳的值将丢弃,头部空出来的位置使用0填充。相当于二进制除2的位数的指数
// 返回 00100010
10001010 >> 2
注意,右移运算符最好只用于无符号整数,不要用于负数。因为不同系统对于右移后如何处理负数的符号位,有不同的做法,可能会得到不一样的结果。
右移运算符>>可以与赋值运算符=结合,简写成>>=。
int val = 1;
val = val >> 2;
// 简写为
val >>= 2;
逗号运算符
逗号运算符用于将多个表达式写在一起,从左到右依次运行每个表达式。
x = 10, y = 20;
上面示例中,有两个表达式(x = 10和y = 20),逗号使得它们可以放在同一条语句里面。
逗号运算符返回最后一个表达式的值,作为整个语句的值。
int x;
x = (1, 2, 3);
上面示例中,括号里面的逗号运算符,返回最后一个表达式的值,所以变量x等于3。
运算优先级
优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不一样的。
3 + 4 * 5;
上面示例中,表达式3 + 4 * 5里面既有加法运算符(+),又有乘法运算符(*)。由于乘法的优先级高于加法,所以会先计算4 * 5,而不是先计算3 + 4。
运算符的优先级顺序很复杂。下面是部分运算符的优先级顺序(按照优先级从高到低排列)。
- 圆括号(
()) - 自增运算符(
++),自减运算符(--) - 一元运算符(
+和-) - 乘法(
*),除法(/) - 加法(
+),减法(-) - 关系运算符(
<、>等) - 赋值运算符(
=)
由于圆括号的优先级最高,可以使用它改变其他运算符的优先级。
int x = (3 + 4) * 5;
上面示例中,由于添加了圆括号,加法会先于乘法进行运算。
完全记住所有运算符的优先级没有必要,解决方法是多用圆括号,防止出现意料之外的情况,也有利于提高代码的可读性。