运算就是用值来做各种变化的游戏,例如 1 + 1 得到 2,其中 + 是加法的运算符。
C 语言使用 = 作为赋值运算符,用于修改变量的值,而非数学中的“等于”。
实例:
#include <stdio.h>
int main(void)
{
int x = 10; // 定义变量 x
printf("%d\n", x);
x = 233; // 修改变量的值
printf("%d\n", x);
return 0;
}
运行结果:
10
233
算术运算符用于进行算术运算。
| 运算符 | 名称 | 示例 | 结果 | 说明 |
|---|---|---|---|---|
+ |
加法运算符 | 3 + 5 |
7 |
|
- |
减法运算符 | 5 - 3 |
2 |
|
* |
乘法运算符 | 5 * 3 |
15 |
|
/ |
除法运算符 | 5 / 2 |
2 |
整型的运算结果是整型,浮点型的运算结果是浮点型 |
% |
取余运算符 | 5 % 2 |
1 |
5 除以 2,得 2 余 1 |
示例:
#include <stdio.h>
int main(void)
{
printf("%d + %d = %d\n", 10, 3, 10 + 3);
printf("%d - %d = %d\n", 10, 3, 10 - 3);
printf("%d * %d = %d\n", 10, 3, 10 * 3);
printf("%d / %d = %d\n", 10, 3, 10 / 3); // 10 除以 3 得 3 余数 1,整型运算结果取整,因此结果为 3
printf("%d %% %d = %d\n", 10, 3, 10 % 3); // 10 除以 3 得 3 余数 1,因此结果为 1
printf("%f / %f = %f\n", 10.0, 3.0, 10.0 / 3.0); // 10.0 除以 3.0 得 3.333333
printf("%f / %d = %f\n", 10.0, 3, 10.0 / 3); // double 与 int 进行运算,int 隐式转换为 double,得 3.333333
return 0;
}
10 / 3 是 int 除以 int,结果取整得 3
10.0 / 3.0 是 double 除以 double,结果是 3.33333
10.0 / 3 是 double 除以 int,int 隐式转换为 double,实际进行 double 除以 double 的运算
运行结果:
10 + 3 = 13
10 - 3 = 7
10 * 3 = 30
10 / 3 = 3
10 % 3 = 1
10.000000 / 3.000000 = 3.333333
10.000000 / 3 = 3.333333
| 运算符 | 名称 | 示例 |
|---|---|---|
+ |
正号 | +3 |
- |
负号 | -5 |
比较运算符用于比较两个值的关系,结果为 true(真) 或 false(假)。
| 运算符 | 名称 | 示例 | 结果 |
|---|---|---|---|
== |
等于运算符 | 3 == 5 |
0 |
!= |
不等于运算符 | 3 != 5 |
1 |
> |
大于运算符 | 5 > 3 |
1 |
< |
小于运算符 | 5 < 2 |
0 |
>= |
大于或等于运算符 | 5 >= 5 |
1 |
<= |
小于或等于运算符 | 5 <= 2 |
0 |
逻辑运算符用于逻辑运算。
| 运算符 | 名称 | 示例 | 结果 | 说明 |
|---|---|---|---|---|
&& |
逻辑与运算符 | true && false |
false |
全部为 true 时结果为 true,否则结果为 false |
\|\| |
逻辑或运算符 | true \|\| false |
true |
全部为 false 时结果为 false,否则结果为 true |
! |
逻辑非运算符 | ! false |
true |
将一个布尔值反转 |
true为 1,false为 0,并且所有非零的值均视为true;参考 布尔类型。
在 C23 标准之前需要包含 stdbool.h 头文件才能直接使用
bool类型以及true和false;
否则,只能使用int类型以及 1 和 0 代替。
示例:
#include <stdio.h>
#include <stdbool.h>
int main(void)
{
// true 和 false 的值是 1 和 0
printf("true = %d\n", true);
printf("false = %d\n", false);
// 逻辑非
printf("!true = %d\n", !true);
printf("!false = %d\n", !false);
// 逻辑与
printf("true && true = %d\n", true && true);
printf("true && false = %d\n", true && false);
printf("false && true = %d\n", false && true);
printf("false && false = %d\n", false && false);
// 逻辑或
printf("true || true = %d\n", true || true);
printf("true || false = %d\n", true || false);
printf("false || true = %d\n", false || true);
printf("false || false = %d\n", false || false);
// 非零值均视为 true
printf("!10 = %d\n", !10);
printf("10 && 10 = %d\n", 10 && 10);
printf("10 && 0 = %d\n", 10 && 0);
printf("10 || 0 = %d\n", 10 || 0);
return 0;
}
运行结果:
true = 1
false = 0
!true = 0
!false = 1
true && true = 1
true && false = 0
false && true = 0
false && false = 0
true || true = 1
true || false = 1
false || true = 1
false || false = 0
!10 = 0
10 && 10 = 1
10 && 0 = 0
10 || 0 = 1
逻辑运算中存在一个名为短路原则(Short-circuit evaluation)的特性——在逻辑运算中,如果前面的部分运算已经能够确定整个运算的结果,就不再计算后面的表达式。
对于 A && B,如果 A 为 false,则无论 B 是什么,整个表达式的结果都必定是 false,因此不会计算 B。
对于 A || B,如果 A 为 true,则无论 B 是什么,整个表达式的结果都必定是 true,因此不会计算 B。
示例:
#include <stdio.h>
#include <stdbool.h>
int main(void)
{
int x = 10;
false && (x = 1); // x = 1 不会被执行
true || (x = 2); // x = 2 不会被执行
printf("x 的值是 %d\n", x);
return 0;
}
运行结果:
x 的值是 10
位运算符用于二进制位运算。
| 运算符 | 名称 | 示例 | 结果 | 说明 |
|---|---|---|---|---|
& |
位与运算符 | 0b1100 & 0b0110 |
0b0100 |
按二进制位进行计算,两个数在某一位上都是 1,则结果在该位为 1,否则结果在该位为 0 |
\| |
位或运算符 | 0b1100 \| 0b0110 |
0b1110 |
按二进制位进行计算,两个数在某一位上都是 0,则结果在该位为 0,否则结果在该位为 1 |
^ |
异或运算符 | 0b1100 ^ 0b0110 |
0b1010 |
二进制中不同的位结果为 1,相同的位结果位 0 |
<< |
左移运算符 | 0b0011 << 2 |
0b1100 |
二进制位向左移动,右侧补 0 |
>> |
右移运算符 | 0b1100 >> 1 |
0b0110 |
二进制位向右移动,左侧补 0 |
~ |
取反运算符 | ~0b1100 |
0b0011 |
二进制位取反,即 1 变为 0,0 变为 1 |
C 语言标准并不支持二进制字面量,表格中示例的二进制写法仅作用演示。
示例:
#include <stdio.h>
int main(void)
{
printf("%#x & %#x = %#x\n", 0xfff0, 0x0fff, 0xfff0 & 0x0fff);
printf("%#x | %#x = %#x\n", 0xfff0, 0x0fff, 0xfff0 | 0x0fff);
printf("%#x ^ %#x = %#x\n", 0xfff0, 0x0fff, 0xfff0 ^ 0x0fff);
printf("%#x << %d = %#x\n", 0xfff0, 4, 0xfff0 << 4);
printf("%#x >> %d = %#x\n", 0xfff0, 4, 0xfff0 >> 4);
printf("~%#x = %#x\n", 0xfff0, ~0xfff0);
return 0;
}
运行结果:
0xfff0 & 0xfff = 0xff0
0xfff0 | 0xfff = 0xffff
0xfff0 ^ 0xfff = 0xf00f
0xfff0 << 4 = 0xfff00
0xfff0 >> 4 = 0xfff
~0xfff0 = 0xffff000f
复合赋值运算符是一种将运算和赋值合并的简化写法,它将变量的值进行计算后再赋值给该变量。例如 x += 1 相当于 x = x + 1。
| 运算符 | 等价写法 | 说明 |
|---|---|---|
+= |
a += b -> a = a + b |
加并赋值 |
-= |
a -= b -> a = a - b |
减并赋值 |
*= |
a *= b -> a = a * b |
乘并赋值 |
/= |
a /= b -> a = a / b |
除并赋值 |
%= |
a %= b -> a = a % b |
取模并赋值 |
<<= |
a <<= b -> a = a << b |
左移并赋值 |
>>= |
a >>= b -> a = a >> b |
右移并赋值 |
&= |
a &= b -> a = a & b |
按位与并赋值 |
\|= |
a \|= b -> a = a \| b |
按位或并赋值 |
^= |
a ^= b -> a = a ^ b |
按位异或并赋值 |
示例:
#include <stdio.h>
int main(void)
{
int x = 10;
x += 2; // x = x + 2 -> 12
printf("%d\n", x);
x -= 3; // x = x - 3 -> 9
printf("%d\n", x);
x *= 4; // x = x * 4 -> 36
printf("%d\n", x);
x /= 5; // x = x / 2 -> 7
printf("%d\n", x);
return 0;
}
运行结果:
12
9
36
7
| 运算符 | 说明 |
|---|---|
x++ |
x = x + 1,返回 x 加一之前的值 |
x-- |
x = x - 1,返回 x 减一之前的值 |
++x |
x = x + 1,返回 x 加一之后的值 |
--x |
x = x - 1,返回 x 减一之后的值 |
示例:
#include <stdio.h>
int main(void)
{
int x = 10;
printf("%d\n", x++); // x = x + 1,返回 x 加一之前的值,因此打印 10
printf("%d\n", x); // x 的值是 11
int y = 10;
printf("%d\n", ++y); // y = y + 1,返回 x 加一之后的值,因此打印 11
printf("%d\n", y); // y 的值是 11
return 0;
}
运行结果:
10
11
11
11
sizeof 运算符可以获取类型或变量的字节数。
示例:
#include <stdio.h>
int main(void)
{
printf("int 的字节数是 %zu\n", sizeof(int)); // 获取 int 类型的字节数
printf("long 的字节数是 %zu\n", sizeof(long)); // 获取 long 类型的字节数
printf("float 的字节数是 %zu\n", sizeof(float)); // 获取 float 类型的字节数
printf("double 的字节数是 %zu\n", sizeof(double)); // 获取 double 类型的字节数
return 0;
}
运行结果:
int 的字节数是 4
long 的字节数是 8
float 的字节数是 4
double 的字节数是 8
注意,C 语言是贴近硬件的底层语言,因此
int等基本类型的大小在不同的环境(硬件、操作系统、编译器等)上可能是不同的。可以通过 limits.h 头文件中的宏获取整数类型的范围。
在标准库头文件 stdint.h 中定义了固定长度的正数类型。
运算符存在优先级,和数学一样先乘除,后加减。例如 \(3 + 5 * 2\) 结果为 \(13\)。
括号为最高优先级,可以使用括号改变计算顺序。例如 \((3 + 5) * 2\) 结果为 \(16\)。
下表为优先级列表,优先级由高到低:
| 优先级 | 运算符 | 结合性 |
|---|---|---|
| 最高 | x++ x-- () [] . -> (type){list} |
从左向右 |
| 2 | ++x --x +x -x (type) * &x sizeof _Alignof |
从右向左 |
| 3 | * / % |
从左向右 |
| 4 | + - |
从左向右 |
| 5 | << >> |
从左向右 |
| 6 | < <= > >= |
从左向右 |
| 7 | == != |
从左向右 |
| 8 | & |
从左向右 |
| 9 | ^ |
从左向右 |
| 10 | \| |
从左向右 |
| 11 | && |
从左向右 |
| 12 | \|\| |
从左向右 |
| 13 | ?: |
从右向左 |
| 14 | = += -= *= /= %= <<= >>= &= ^= \|= |
从右向左 |
| 最低 | , |
从左向右 |