运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。
算术运算符
使用 C 程序对变量进行基本的算术运算,例如:
#include<stdio.h>
int main() {
int a = 100, b = 200;
printf("a + b = %d.\n", a + b);
printf("a - b = %d.\n", a - b);
printf("a * b = %d.\n", a * b);
printf("a / b = %d.\n", a / b);
printf("a %% b = %d.\n", a % b);
printf("a++ = %d.\n", a++);
printf("a-- = %d.\n", a--);
printf("++a = %d.\n", ++a);
printf("--a = %d.\n", --a);
return 0;
}
执行结果:
a + b = 300.
a - b = -100.
a * b = 20000.
a / b = 0.
a % b = 100.
a++ = 100.
a-- = 101.
++a = 101.
--a = 100.
其中 ++
和 --
也称之为自运算符,分别是自增运算和自减运算。如上例所示,++a
等价于 a = a + 1
,同样的,a++
等价于 a = a + 1
。符号 ++
无论是在变量 a 的前面还是后面,结果都表示将变量 a + 1 后重新赋值给变量 a。
虽然最终变量 a 的值是一样的,但是自增运算符在变量前和变量后得到的表达式的结果是不一样的。如下例所示:
#include<stdio.h>
int main() {
int a = 10, b;
b = ++a;
printf("a = %d, b = %d\n", a, b);
int c = 10, d;
d = c++;
printf("c = %d, d = %d\n", c, d);
return 0;
}
执行结果:
a = 11, b = 11
c = 11, d = 10
从该例中可以知道的是,无论使用 a++
还是 ++a
最终变量 a 的值都会被自增,结果是没有区别的。区别在于表达式的结果是不一样的,在 c++
表达式中,表达式的结果(也就是例中的 变量 d )的值是变量 c 自增前的值,而在 ++a
表达式中,表达式的结果(也就是例中的变量 b)的值是变量 a 自增后的值。故而,得到结论是:自运算表达式中,当自运算符作为前缀时,先执行自运算,再将自运算结果作为表达式的结果,当自运算符作为后缀时,先取得变量的值作为表达式的结果,再执行自运算。简记为:
- 自运算符作为前缀时:先运算,后取值
- 自运算符作为后缀时:先取值,后运算
关系运算符
关系运算符得到的结果一般是布尔类型,值为 true 或者 false,即真或假。在 C 中,一般使用数字 0
表示假,使用数字 1
表示真,当然,使用其他数字如 2,3,4 也可以表示真,但一般不这样做;简记为 非零即真。案例如下:
#include<stdio.h>
int main() {
int a = 100, b = 200;
printf("a == b is %d.\n", a == b);
printf("a != b is %d.\n", a != b);
printf("a > b is %d.\n", a > b);
printf("a < b is %d.\n", a < b);
printf("a >= b is %d.\n", a >= b);
printf("a <= b is %d.\n", a <= b);
return 0;
}
执行结果:
a == b is 0.
a != b is 1.
a > b is 0.
a < b is 1.
a >= b is 0.
a <= b is 1.
三元运算符
类似 Excel 中的 IF
函数,基本格式是: 条件表达式 ? 真值结果 : 假值结果
, 据此,修改上面的关系运算符案例如下:
#include<stdio.h>
int main() {
int a = 100, b = 200;
printf("a == b is %s.\n", a == b ? "true" : "false");
printf("a != b is %s.\n", a != b ? "true" : "false");
printf("a > b is %s.\n", a > b ? "true" : "false");
printf("a < b is %s.\n", a < b ? "true" : "false");
printf("a >= b is %s.\n", a >= b ? "true" : "false");
printf("a <= b is %s.\n", a <= b ? "true" : "false");
return 0;
}
执行结果:
a == b is false.
a != b is true.
a > b is false.
a < b is true.
a >= b is false.
a <= b is true.
逻辑运算符
在 C 中,逻辑运算符主要是 与(&&
)或(||
)非(!
)三种,它主要是对布尔值进行运算。因为在 C 中默认没有定义 boolean 类型,结合上述 非零即真 的思想,可以使用 0 和 1 类模拟布尔运算,案例如下:
#include<stdio.h>
#define true 1
#define false 0
int main() {
printf("true && true is %s.\n", true && true ? "true" : "false");
printf("true && false is %s.\n", true && false ? "true" : "false");
printf("false && true is %s.\n", false && true ? "true" : "false");
printf("false && false is %s.\n", false && false ? "true" : "false");
printf("true || true is %s.\n", true || true ? "true" : "false");
printf("true || false is %s.\n", true || false ? "true" : "false");
printf("false || true is %s.\n", false || true ? "true" : "false");
printf("false || false is %s.\n", false || false ? "true" : "false");
printf("!true is %s.\n", !true ? "true" : "false");
printf("!false is %s.\n", !false ? "true" : "false");
return 0;
}
执行结果:
true && true is true.
true && false is false.
false && true is false.
false && false is false.
true || true is true.
true || false is true.
false || true is true.
false || false is false.
!true is false.
!false is true.
位运算符
对变量的二进制值进行按位与(&
),按位或(|
),按位异或(^
),位取取反(~
),左移运算(<<
)和右移运算(>>
)。因为二进制值只有 0 和 1,其运算的真值表参考下表:
P | Q | P & Q | P | Q | P ^ Q | ~P | ~Q |
---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 | 1 |
0 | 1 | 0 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 | 0 | 1 |
1 | 1 | 1 | 1 | 0 | 0 | 0 |
在进行位运算的时候,可以将 1 视为 真, 将 0 视为假,可以简记为:
- 按位与运算 —— 同真得真,存假得假
- 按位或运算 —— 存真得真,全假得假
- 按位异或运算 —— 同得假,异得真
简单的位运算案例如下:
#include<stdio.h>
int main() {
int a = 100, b = 200;
// 二进制 十进制
// a 01100100 100
// b 11001000 200
// & 01000000 64
// | 11101100 236
// ^ 10101100 172
printf("a & b = %d.\n", a & b);
printf("a | b = %d.\n", a | b);
printf("a ^ b = %d.\n", a ^ b);
printf("~a = %d.\n", ~a);
printf("~b = %d.\n", ~b);
return 0;
}
执行结果:
a & b = 64.
a | b = 236.
a ^ b = 172.
~a = -101.
~b = -201.
左移运算和右移运算
将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。为了弄明白这个左移运算,可以尝试将某个数字转换为二进制之后,将其进行左移,然后补零,得到的新的二进制数后重新转换为十进制。这样做比较麻烦,这里使用一个简单的案例来更好地理解左移运算:
#include<stdio.h>
int main() {
int a = 1;
printf("%d << 1 = %d\n", a, a << 1);
printf("%d << 2 = %d\n", a, a << 2);
printf("%d << 3 = %d\n", a, a << 3);
printf("%d << 4 = %d\n", a, a << 4);
printf("%d << 5 = %d\n", a, a << 5);
return 0;
}
执行结果:
1 << 1 = 2
1 << 2 = 4
1 << 3 = 8
1 << 4 = 16
1 << 5 = 32
可以发现运算结果是有规律的,分别像是 2 的左移数次幂。改变变量 a 的值为7之后重试,结果如下:
7 << 1 = 14
7 << 2 = 28
7 << 3 = 56
7 << 4 = 112
7 << 5 = 224
所以,在左移运算中有:某个整数 n 在执行左移运算的时候,若左移数为 m,则运算结果为 :n × 2m
同理,在右移运算中有:某个整数 n 在执行右移运算的时候,若右移数为 m,则运算结果为 :n ÷ 2m
这两条结论适用于整数,那要是小数呢?修改源程序中的 int 为 double 或者 float,会发现在程序编译的时候提示:invalid operands to binary >>
所以,得到结论:位移运算一般适用于整数类型的运算中。
赋值运算符
赋值运算符是 C 语言中最常见的运算符,赋值运算符左边的量的值称之为左值,右边的量的值称之为右值。赋值运算的目的是将右值赋予左边的量。最基本的赋值运算符是 =
,例如:
int a = 10;
这行代码的含义在之前的《变量&常量》有提及,与之类似的还有 +=
, -=
, *=
, /=
, %=
, &=
, ^=
, |=
, <<=
, >>=
, 例如 a += 100;
案例如下:
#include<stdio.h>
int main() {
int a = 10;
a += 100;
printf("a = %d\n", a);
return 0;
}
执行结果:
a = 110
由此可知,a += 10
等价于 a = a + 10
,同理可得如下结论:
a += 10
等价于a = a + 10
a -= 10
等价于a = a - 10
a *= 10
等价于a = a * 10
a /= 10
等价于a = a / 10
a %= 10
等价于a = a % 10
a &= 10
等价于a = a & 10
a |= 10
等价于a = a | 10
a ^= 10
等价于a = a ^ 10
a <<= 10
等价于a = a << 10
a >>= 10
等价于a = a << 10
其他运算符
sizeof
sizeof 用于 返回变量或者数据类型的大小,即占用字节的位数。例如:
#include<stdio.h>
int main() {
int a = 7;
printf("sizeof(a) is %d.\n", sizeof(a));
printf("sizeof(int) is %d.\n", sizeof(int));
printf("sizeof(long) is %d.\n", sizeof(long));
printf("sizeof(double) is %d.\n", sizeof(double));
return 0;
}
执行结果:
sizeof(a) is 4.
sizeof(int) is 4.
sizeof(long) is 4.
sizeof(double) is 8.
这里的结果主要取决于机器的字长
逗号 (,)
逗号是一个运算优先级最低的运算符,目前主要出现在赋值表达式和循环结构中。如下例:
#include<stdio.h>
int main() {
int a = (5, 6), b = (3 + 4, 7 + 8);
printf("a=%d b=%d\n", a, b);
for(int i = 1, j = 3; i < 4; i++, j--) {
printf("i=%d j=%d\n", i, j);
}
return 0;
}
执行结果:
a=6 b=15
i=1 j=3
i=2 j=2
i=3 j=1
该程序中,5, 6
,3 + 4, 7 + 8
,i++, j--
中的逗号都表示一个运算符。它在目前的程序设计语言中没有太实际的含义,其目的是为了将一个表达式分割为多个子表达式,从而将最后一个表达式的结果作为运算的结果。例如 5, 6
的结果是 6,3 + 4, 7 + 8
的结果是 15,而 i++, j--
的结果是 j--
表达式的结果,只不过这里没有变量来接收,可以调整代码使其有一个变量接收运算的结果。如下:
#include<stdio.h>
int main() {
int a = (5, 6), b = (3 + 4, 7 + 8);
printf("a=%d b=%d\n", a, b);
for(int i = 1, j = 3; i < 4; ) {
int c = (i++, j--);
printf("i=%d j=%d c=%d\n", i, j, c);
}
return 0;
}
执行结果:
a=6 b=15
i=2 j=2 c=3
i=3 j=1 c=2
i=4 j=0 c=1
取址符(&)
该符号用于读取变量的内存地址,例如:
#include<stdio.h>
int main() {
int a = 100;
printf("a = %d, &a = %d", a, &a);
return 0;
}
执行结果:
a = 100, &a = 2293564
取值符(*)
主要用于指针,读取指针存储的地址值对应的值。例如:
#include<stdio.h>
int main() {
int a = 100;
printf("a = %d, &a = %d\n", a, &a);
int *p = &a;
printf("p = %d, *p = %d\n", p, *p);
return 0;
}
执行结果:
a = 100, &a = 2293560
p = 2293560, *p = 100
运算符的优先级
这里以表格的形式展示 C 运算符的优先级,从上往下,优先级依次降低。
运算符 | 运算方向 | 描述 |
---|---|---|
() [] -> . ++ -- | → | 这里的自运算符作为后缀 |
+ - ! ~ ++ -- * & sizeof | ← | 一元运算 |
* / % + - | → | 算数运算 |
<< >> | → | 位移运算 |
< <= > >= == != | → | 关系运算 |
& ^ | | → | 按位 与,异或,或 运算 |
&& || | → | 逻辑 与,或 运算 |
? : | ← | 三元条件运算 |
= += -= *= /= %= >>= <<= &= ^= |= | ← | 赋值运算 |
, | → | 逗号 |
说明:
()
是优先级最高的运算符,故而,在很多时候,可以使用它修改表达式的优先级。- 自运算符作为前缀和后缀的运算优先级是不一样的,后缀优先级高于前缀。
- 在表格的一元运算所在行中,
&
用于变量取址,*
用于指针取值,++
和--
作为自运算前缀符,~
是按位取反运算,!
是逻辑非运算符,+
和-
分别表示的数的正负性。