算术运算符
算术运算符用于执行基本的数学运算。
1. 加法类运算符 +
-
1.1 加法 +
+
是一个二元运算符,用于计算两个数的和。
int sum = 10 + 5; // sum 的值为 15
double total = 3.14 + 2.5; // total 的值为 5.64
2
1.2 减法 -
-
是一个二元运算符,用于计算两个数的差。
int difference = 10 - 5; // difference 的值为 5
2. 乘法类运算符 *
/
%
2.1 乘法 *
*
是一个二元运算符,用于计算乘法。
int product = 6 * 3; // product 的值为 18
2.2 除法 /
/
是一个二元运算符,用于计算除法。它的行为取决于操作数的数据类型。
整数除法:当两个操作数都是整数时,结果也是整数,小数部分会被向零取整(直接舍弃)。
cint a = 6 / 3; // a = 2 int b = 5 / 3; // b = 1 (小数部分 .666... 被舍弃) int c = -6 / 2; // c = -3 int d = -5 / 2; // d = -2 (小数部分 .5 被舍弃)
1
2
3
4浮点数除法:如果操作数中至少有一个是浮点数(如
float
或double
),则执行浮点数除法,结果是浮点数。cdouble e = 5.0 / 2.0; // e = 2.5 double f = 5 / 2.0; // f = 2.5 (整数 5 会被提升为 double 类型)
1
2
2.3 取模 (取余) %
%
是一个二元运算符,用于计算两个整数相除的余数。
类型要求
%
运算符的操作数必须是整数。
int remainder1 = 5 % 3; // remainder1 的值为 2
int remainder2 = 6 % 3; // remainder2 的值为 0
int remainder3 = 7 % 3; // remainder3 的值为 1
2
3
对于负数,结果的符号与被除数(第一个操作数)的符号相同(C99 标准及以后)。
int r1 = -5 % 3; // r1 的值为 -2
int r2 = 5 % -3; // r2 的值为 2
2
3. 一元运算符 +
-
++
--
一元运算符只作用于一个操作数。
3.1 一元正负号
- 一元加号
+
:返回操作数的值,基本不起作用。 - 一元减号
-
:返回操作数的相反数。
int a = 10;
int b = -a; // b 的值为 -10
int c = +a; // c 的值为 10
2
3
3.2 自增 ++
和自减 --
这两个运算符用于将变量的值加 1 或减 1,有前缀和后缀两种形式。
前缀形式 (Pre-increment/decrement):
++a
或--a
。先将变量a
的值加 1 或减 1,然后使用新值参与表达式的运算。cint a = 5; int b = ++a; // 先执行 a = a + 1; 此时 a 变为 6 // 再执行 b = a; // 最终结果: a = 6, b = 6
1
2
3
4后缀形式 (Post-increment/decrement):
a++
或a--
。先使用变量 a 的原始值参与表达式的运算,然后再将a
的值加 1 或减 1。cint a = 5; int b = a++; // 先执行 b = a; 此时 b 获得 a 的原始值 5 // 再执行 a = a + 1; a 变为 6 // 最终结果: a = 6, b = 5
1
2
3
4
4. 位运算符
位运算符直接对整数在内存中的二进制位进行操作。这在底层编程、硬件控制和性能优化等场景中非常有用。
类型要求
位运算符只能用于整型数据(如 char
, short
, int
, long
等)。
下面的例子默认使用无符号 8 位整数 (
uint8_t
) 进行演示。
4.1 位运算符的种类
按位与 (
&
):对应位都为1
时,结果位才为1
,否则为0
。txt10011010 & 10110110 ---------- 10010010
1
2
3
4复合赋值运算符:
&=
按位或 (
|
):对应位只要有一个为1
,结果位就为1
。txt10011010 | 10110110 ---------- 10111110
1
2
3
4复合赋值运算符:
|=
按位异或 (
^
):对应位不同时(一个为1
一个为0
),结果位为1
,否则为0
。txt10011010 ^ 10110110 ---------- 00101100
1
2
3
4复合赋值运算符:
^=
按位取反 (
~
):将所有的0
变为1
,所有的1
变为0
。这是一个一元运算符。txt~ 10011010 ---------- 01100101
1
2
3按位左移 (
<<
):将一个数的所有位向左移动指定的位数,右边空出的位用0
填充。左移 N 位相当于乘以 2 的 N 次方。c// 将 10011010 左移 2 位 (10011010) << 2 // 表达式 01101000 // 结果值 (左边溢出的位被丢弃)
1
2
3复合赋值运算符:
<<=
按位右移 (
>>
):将一个数的所有位向右移动指定的位数。右移 N 位相当于除以 2 的 N 次方。左边空出的位的填充方式取决于操作数的类型:无符号数 (Unsigned):进行逻辑右移,左边空出的位总是用
0
填充。c// 将无符号数 10011010 右移 2 位 (10011010) >> 2 // 表达式 00100110 // 结果值
1
2
3有符号数 (Signed):行为是实现定义的 (implementation-defined),可以是逻辑右移或算术右移。
- 算术右移:左边空出的位用原符号位填充,以保持数的正负性质不变。这是绝大多数现代编译器的做法。
c// 假设对有符号数 10011010 (负数) 进行算术右移 2 位 (10011010) >> 2 11100110 // 结果值 (左边用符号位 1 填充)
1
2
3
复合赋值运算符:
>>=
4.2 位操作的应用
在实际项目中,尤其是在嵌入式和硬件开发中,位操作非常常见。
注:为了代码清晰和可移植性,推荐使用
stdint.h
头文件中定义的固定宽度整数类型,如uint8_t
(无符号8位整型),uint32_t
(无符号32位整型) 等。
4.2.1 掩码 (MASK)
定义:掩码是一个预设的二进制数,用于选择、屏蔽或修改另一个二进制数的特定位。 作用:通过按位与 &
操作,可以像滤网一样,只保留原始数据中我们感兴趣的位。
#include <stdint.h>
uint8_t flag = 0b10101010; // 原始数据
// 定义 MASK,我们只关心第 1 位 (从右数,第0位开始)
uint8_t mask = 0b00000010; // 也可以写作 0x02
// 使用掩码提取 flag 的第 1 位
uint8_t result = flag & mask; // result = 0b00000010
2
3
4
5
6
7
8
4.2.2 打开/设置位 (SET)
定义:将一个数的特定位设置为 1
,同时保持其它位不变。 方法:使用按位或 |
和掩码。任何位与 1
进行或运算结果都是 1
;与 0
进行或运算结果保持不变。
uint8_t flag = 0b10101001;
uint8_t mask = 0b00000010; // 目标:打开第 1 位
flag |= mask; // 等价于 flag = flag | mask;
// 此时 flag = 0b10101011
2
3
4
5
4.2.3 关闭/清空位 (CLEAR)
定义:将一个数的特定位设置为 0
,同时保持其它位不变。 方法:使用按位与 &
和 按位取反 ~
。首先对掩码取反,使目标位变为 0
,其他位变为 1
。然后进行与运算。
uint8_t flag = 0b10101011;
uint8_t mask = 0b00000010; // 目标:关闭第 1 位
flag &= ~mask; // 等价于 flag = flag & (~mask);
// ~mask 的结果是 0b11111101
// flag & (~mask) 之后,flag = 0b10101001
2
3
4
5
6
4.2.4 切换/翻转位 (TOGGLE)
定义:将特定位进行翻转(1
变0
,0
变1
),同时保持其它位不变。 方法:使用按位异或 ^
和掩码。任何位与 1
异或会翻转;与 0
异或则保持不变。
uint8_t flag = 0b10101011;
uint8_t mask = 0b11110000; // 目标:翻转高 4 位
flag ^= mask; // 等价于 flag = flag ^ mask;
// 此时 flag = 0b01011011
2
3
4
5
4.2.5 读取/检查位 (READ)
定义:检查特定一位或几位的值是 1
还是 0
。 方法:使用按位与 &
和掩码,然后判断结果。
uint8_t flag = 0b10101010;
uint8_t mask = 0b00000010; // 目标:检查第 1 位
// 方法1:检查结果是否不为 0
if ((flag & mask) != 0) {
// 第 1 位是 1
}
// 方法2:检查结果是否等于掩码本身 (适用于检查单一位)
if ((flag & mask) == mask) {
// 第 1 位是 1
}
2
3
4
5
6
7
8
9
10
11
12