【C】C系列三 - 运算符

C 语言中常用的运算符,如下表所示

一、算术运算符

1.1. 加减乘除

1
2
3
int a = 10;
int b = 20;
printf("%d %d %d %d\n", a + b, a - b, a * b, a / b); // 输出:30 -10 200 0

注意:乘法 ab,并不是 axb;除法 a/b,并不是 a÷b*

1
2
3
int a = 10;
int b = -20;
printf("%d %d %d %d\n", +a, -a, +b, -b); // 输出:10 -10 -20 20

1.1.1. 整数与整数进行运算,结果依然是整数:

1
printf("%d\n", 7 / 4); // 输出:1

整数除以整数时,直接剔除小数部分,不会四舍五入

1.1.2. 浮点数与浮点数进行运算,结果依然是浮点数:

1
printf("%f\n", 7.0 / 4.0); // 输出:1.750000

1.1.3. 整数与浮点数进行运算,结果是浮点数:

1
2
3
4
5
6
7
8
9
10
11
// 以下结果全部输出:1.750000
printf("%f\n", 7.0 / 4);
printf("%f\n", 7 / 4.0);
printf("%f\n", (double)7 / 4);
printf("%f\n", 7 / (double)4);
printf("%f\n", (double)7 / (double)4);

// 注意点
printf("%f\n", (double)(7 / 4)); // 输出:1.000000
printf("%d", 3333 / 1000 * 1000); // 输出:3000
printf("%d", 2 / 0); // 错误写法(编译器会报警告,部分编译器会输出NaN)

1.2. 模

模运算符的作用是求余数,所以,也叫做求余运算符、取余运算符、余数运算符。

1
2
3
4
5
printf("%d\n", 20 % 5); // 输出:0
printf("%d\n", 21 % 5); // 输出:1
printf("%d\n", 22 % 5); // 输出:2
printf("%d\n", 23 % 5); // 输出:3
printf("%d\n", 24 % 5); // 输出:4

1.2.1. 模运算符不能用在浮点数上,否则编译器会报错:

1
2
3
printf("%d\n", 10.0 % 5);
printf("%d\n", 10 % 5.0);
printf("%d\n", 10.0 % 5.0);

1.2.2. 同样的,0 不能作为被除数取余:

1
printf("%d", 10 / 0);

1.2.3. 模运算结果的正负性跟随运算符左边的操作数:

1
2
3
4
printf("%d\n", 10 % 7); // 输出:3
printf("%d\n", 10 % -7); // 输出:3
printf("%d\n", -10 % 7); // 输出:-3
printf("%d\n", -10 % -7); // 输出:-3

案例:提示用户输入秒数,输出格式 00 时 00 分 00 秒

1
2
3
4
5
6
7
8
9
10
11
printf("请输入秒数:");
int input;
scanf("%d", &input);
int hour = input / 3600;
int minutes = input / 60 - hour*60;
int seconds = input % 60;
printf("%02d时%02d分%02d秒\n", hour, minutes, seconds);
/*
输入:7592
输出:02时06分32秒
*/

1.3. 隐式类型转换

1.3.1. 在进行一些算数运算时,小类型会被隐式转换成大类型

1
char < short < int < long < long long < float < double < long double

案例:

1
2
3
4
5
6
double d = 10.5;
int i = 20;
printf("%f\n", d + i); // 输出:30.500000

long l = 10L;
printf("%ld\n", l + i); // 输出:30

1.3.2. 任何小于 int 的整数类型,在运算时会隐式转换为 int 类型

案例:

1
2
3
4
5
char c = 97;
short s = 10;
printf("%zd %zd\n", sizeof(c), sizeof(s)); // 输出:1 2
printf("%zd %zd\n", sizeof(+c), sizeof(-s)); // 输出:4 4
printf("%zd %zd\n", sizeof(c + s), sizeof(c / s)); // 输出: 4 4

经典案例:

1
printf("%zd\n", sizeof('A')); // 输出:4

为什么会输出 4 呢?因为在 C 语言中,字符的本质是整数,而我们没有声明要把这个字符当 char 来使用,所以是 4 个字节。

1.4. 运算符的优先级

当一个表达式中同时使用多个运算符时,会根据运算符的优先级结合性,来决定运算符的执行顺序。

1
2
3
printf("%d\n", 4 + 2 - 1); // 输出:5
printf("%d\n", 4 * 2 / 2); // 输出:4
printf("%d\n", 4 + 2 * 3 - 3 / 2); // 输出:9
  • 优先级越高(优先级值越小),越先被执行;
  • 优先级一样,根据结合性决定执行顺序。


案例:
可以使用小括号来调整优先级

1
2
3
4
int a = 1, b = 2, c = 3;
printf("%d\n", a + b * c); // 输出:7
printf("%d\n", (a + b) * c); // 输出:9
printf("%d\n", ((a / b) * b) + (a % b)); // 输出:1

当自己记不住(不确定)运算符的优先级时,多使用小括号即可,根本不用刻意去背运算符的优先级。当然,为了代码的可读性,也应该多使用小括号。

1
2
3
int a = 1, b = 2, c = 3;
printf("%d\n", a > 0 ? a + b ? a - b : b + c : b - c); // 输出:-1
printf("%d\n", (a > 0) ? ((a + b) ? (a - b) : (b + c)) : (b - c)); // 输出:-1

二、赋值运算符


案例:

1
2
3
int a = 40, b = 30, c = 20, d = 10;
int e = a -= b = c += d %= 8;
printf("%d %d %d %d %d\n", a, b, c, d, e); // 输出:18 22 22 2 18

2.1. 自增/自减运算符

自增/自减运算符包括:

  • 前缀
    • 自增运算符:++a
    • 自减运算符:–a
  • 后缀
    • 自增运算符:a++
    • 自减运算符:a–
1
2
3
4
5
6
7
8
9
10
11
12
int a = 1;
++a; // 等价于 a += 1;
printf("%d\n", a); // 输出:2

a++; // 等价于 a += 1;
printf("%d\n", a); // 输出:3

--a; // 等价于 a -= 1;
printf("%d\n", a); // 输出:2

a--; // 等价于 a -= 1;
printf("%d\n", a); // 输出:1

案例一:

1
2
3
4
5
6
7
8
9
int a = 1, b = 3;
b = ++a;
printf("%d %d", a, b); // 输出:2 2
/*
b = ++a;
等效代码:
a += 1;
b = a;
*/

案例二:

1
2
3
4
5
6
7
8
9
int a = 1, b = 3;
b = a++;
printf("%d %d", a, b); // 输出:2 1
/*
b = a++;
等效代码:
b = a;
a += 1;
*/

注意:以下代码不容易理解,可读性差,结果具有不确定性(有些编译器会报警告),所以不建议在实际开发中编写此类代码。

1
2
3
4
5
6
7
8
9
int a = 1;
int b = ++a + ++a + ++a + ++a;
printf("%d %d\n", a, b);
/*
输出:
微软编译器:b = 5 + 5 + 5 + 5 = 20
GCC:b = 3 + 3 + 4 + 5 = 15
Clang:b = 2 + 3 + 4 + 5
*/

2.2. 最大吞噬规则

当多个运算符紧挨在一起时,编译器会按照最大吞噬规则去解析。

案例一:

1
2
3
int a = 1, b = 2;
int c = a +++ b; // 等价代码:int c = (a++) + b
printf("%d %d %d\n", a, b, c); // 输出:2 2 3

案例二(错误代码):

1
2
3
4
5
6
7
int a = 1, b = 2;
int c = a +++++ b;
/*
等价代码:
int c = (a++) ++ + (b);
编译器不知道中间的++是作用于a还是b,所以就会在编译阶段报错
*/

三、比较运算符


比较运算符,也称为关系运算符,运算结果只可能 shi 整数 0 和 1。

  • 当指定关系成立时,就返回整数 1,表明是真的,正确的;
  • 当指定关系不成立时,就返回整数 0,表明是假的,错误的。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
int a = 10;
int b = 8;
printf("%d\n", a > 5); // 输出:1
printf("%d\n", a >= b); // 输出:1
printf("%d\n", a != b); // 输出:1
printf("%d\n", a < 6); // 输出:0
printf("%d\n", a <= 3); // 输出:0
printf("%d\n", a == 13); // 输出:0

printf("%d\n", 3 > 4 + 7); // 输出:0
/*
输出:0
分解:
3 > 4 + 7
3 > 11
0
*/

printf("%d\n", (3 > 4) + 7);
/*
输出:7
分解:
(3 > 4) + 7
0 + 7
7
*/

printf("%d\n", 5 != 4 + 2 * 7 > 3 == 10);
/*
输出:0
分解:
5 != 4 + 2 * 7 > 3 == 10
5 != 4 + 14 > 3 == 10
5 != 18 > 3 == 10
5 != 1 == 10
1 == 10
0
*/

四、逻辑运算符

在 C 语言中,任何值都有真假性:

  • 任何非 0 的值都为“真”(比如 10、1、19、-92、”123”、”kk”、’a’等);
  • 只有数值 0 才为“假”(比如 0、0.0、’\0’(空字符)等)。

逻辑运算符的运算数可以是任何值,运算结果要么是真、要么是假:

  • 运算结果为真,返回整数 1
  • 运算结果为假,返回整数 0

4.1. 逻辑非(!)

!a(对 a 的真假进行取反,就是说,真的变假,假的变真):

  • 如果 a 是真,则!a 的运算结果是假,返回整数 0;
    如果 a 是假,则!a 的运算结果是真,返回整数 1;
  • 优先级:逻辑非 > 加减乘除 > 关系运算符;
  • 连续偶数个!!相当于没有做任何取反;
  • 字符串不管是否包含字符(即长度>=0),一定是真。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int a = 10;
printf("%d\n", !a); // 输出:0
printf("%d\n", !100); // 输出:0
printf("%d\n", !-20); // 输出:0
printf("%d\n", !0); // 输出:1
printf("%d\n", !+0); // 输出:1
printf("%d\n", !-0); // 输出:1
printf("%d\n", !'a'); // 输出:0
printf("%d\n", !"idbeny"); // 输出:0
printf("%d\n", !0.0); // 输出:1
printf("%d\n", !0.0001); // 输出:0
printf("%d\n", !0.1); // 输出:0
printf("%d\n", !1.0); // 输出:0

printf("%d\n", !a>5); // 输出:0
printf("%d\n", (!a)>5); // 输出:0
printf("%d\n", !(a>5)); // 输出:0
printf("%d\n", !(a < 5) * 2 + !(a / 20)); // 输出:3
/*
输出:3
分解:
!(a < 5) * 2 + !(a / 20)
!0 * 2 + !0
1 * 2 + 1
3
*/
printf("%d\n", !!(a < 20)); // 输出:1
printf("%d\n", !!!(a < 20)); // 输出:0

printf("%d\n", !'0'); // 输出:0
printf("%d\n", !'\0'); // 输出:1
printf("%d\n", !""); // 输出:0
printf("%d\n", !"0"); // 输出:0
printf("%d\n", !"000"); // 输出:0

4.2. 逻辑与(&&)

a && b

  • 只有 a、b 都为真时,运算结果才为真,返回整数 1
  • 只要 a、b 其一为假,运算结果就为假,返回整数 0
  • 在表示范围时,逻辑与(&&)是取交集(∩),exp(a) ∩ exp(b)
  • 短路现象:如果多个条件同时判断,只要前面条件判断为假,后面条件不会继续执行,直接跳过
  • 优先级:加减乘除 > 关系运算符 > 逻辑与

案例一:

1
2
3
4
5
6
7
8
9
10
int a = 10;
int b = -20;
printf("%d\n", a && b); // 输出:1
printf("%d\n", a && 0); // 输出:0
printf("%d\n", b && 0.0); // 输出:0
printf("%d\n", !a && b); // 输出:0
printf("%d\n", a && !'\0'); // 输出:1
printf("%d\n", !(a && 'k')); // 输出:0
printf("%d\n", a > 3 && b < 0); // 输出:1
printf("%d\n", (a > 3) && (b < 0)); // 输出:1

案例二(逻辑与的短路现象):

1
2
3
int a = 1, b =2, c = 3, d = 4, m = 2, n = 2;
printf("%d\n", (m = a > b) && (n = c > d)); // 输出:0
printf("%d %d\n", m, n); // 输出:0 2

4.3. 逻辑或(||)

a || b

  • 只要 a、b 其一为真,运算结果就为真,返回整数 1;
  • 只有 a、b 都为假时,运算结果才为假,返回整数 0;
  • 在表示范围时,逻辑或(||)是取并集(∪),exp(a) ∪ exp(b)
  • 短路现象:如果多个条件同时判断,只要前面条件判断为真,后面条件不会继续执行,直接跳过;
  • 优先级:加减乘除 > 关系运算符 > 逻辑与 > 逻辑或。

案例一:

1
2
3
4
5
6
7
8
9
10
int a = 10;
int b = -20;
printf("%d\n", a || b); // 输出:1
printf("%d\n", a || 0); // 输出:1
printf("%d\n", a || 0.0); // 输出:1
printf("%d\n", !a || b); // 输出:1
printf("%d\n", a || !'\0'); // 输出:1
printf("%d\n", !(a || 'a')); // 输出:0
printf("%d\n", a > 3 || b < 0); // 输出:1
printf("%d\n", (a > 3) || (b < 0)); // 输出:1

案例二(逻辑或的短路现象):

1
2
3
int a = 1, b =2, c = 3, d = 4, m = 2, n = 2;
printf("%d\n", (m = a < b) || (n = c > d)); // 输出:1
printf("%d %d\n", m, n); // 输出:1 2

五、条件运算符

条件运算符,一般也叫做三目运算符或三元运算符。

格式:

1
a ? b : c
  • 如果 a 为真,返回 b
  • 如果 a 为假,返回 c

案例一:

1
2
3
int a = 5 ? 1 : 2;
int b = 0 ? 1 : 2;
printf("%d %d\n", a, b); // 输出:1 2

案例二:

1
2
3
4
5
6
7
8
9
int a = 1, b = 2, c = 3;
// 易读性差
printf("%d\n", a > 0 ? a + b ? a - b : b + c : b - c); // 输出:-1

// 相比第一种好,但易读性还是相对较差
printf("%d\n", a > 0 ? (a + b ? a - b : b + c) : b - c); // 输出:-1

// 推荐写法
printf("%d\n", (a > 0) ? ((a + b) ? (a - b) : (b + c)) : (b - c)); // 输出:-1

案例三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
场景:有一个每行8列的表格,每个格子只能存放一个数值,如果一共有n个数值需要存放,请问这个表格至少需要多少行?
问题等价:如果一共有n条数据需要分页显示,每一页最多8条数据,求一共有多少页数据?
*/
printf("请输入数据总条数:");
int input = 0;
scanf("%d", &input);
printf("请输入每页最多展示数据条数(默认:8条):");
int cols = 8;
scanf("%d", &cols);
// 思想:input % cols 的结果如果是0,代表没有多余数据,每页全部占满;如果结果是1,代表有不足cols条数的数据,并且需要新增一行来显示
int rows = (input % cols) ? (input / cols + 1) : (input / cols);
// 等价计算(这种方式小心内存溢出风险)
// int rows = (input + cols - 1) / cols;
printf("一共%d页数据\n", rows);
/*
请输入数据总条数:7
请输入每页最多展示数据条数(默认:8条):8
一共1页数据

请输入数据总条数:99
请输入每页最多展示数据条数(默认:8条):8
一共13页数据
*/

六、逗号运算符

表达式(a, b, ...),从左到右依次执行表达式,返回最后一个表达式的运算结果。

1
2
3
4
5
6
7
8
9
10
11
12
int a = (10, 20, 30);
int b = (a += 2, a = a > 0 ? 100 : -100, a++);
printf("%d %d\n", a, b); // 输出:101 100

// 等效代码
10;
20;
int a = 30;
a += 2;
a = a > 0 ? 100 : -100
int b = a++;
printf("%d %d\n", a, b); // 输出:101 100

七、位运算符

位运算符属于算术运算符,位运算符的运算数只能是整数。

7.1. 位移运算符

位移运算符包含按位左移(也叫逐位左移)按位右移(也叫逐位右移),移动的位数不能是负数。

7.1.1. 按位左移(也叫逐位左移)

格式:a << b
特点:a 左移 b 位,低位补 0

案例一:

1
2
int a = 5;
printf("%d\n", a << 3); // 输出:40

整数 5 向左移 3 位后
$$2^0 + 2^2 => 2^3 + 2^5 = (2^0 + 2^2) * 2^3$$

案例二:

1
2
int a = -5;
printf("%d\n", a << 3); // 输出:-40

a << b结果:$a*2^b$

7.1.2. 按位右移(也叫逐位右移)

格式:a >> b
特点:a 右移 b 位,高位用符号位填充

案例一:

1
2
int a = 40;
printf("%d\n", a >> 3); // 输出:5

整数 40 向右移 3 位后
$$2^3 + 2^5 => 2^0 + 2^2 = (2^3 + 2^5) * 2^{-3} = (2^3 + 2^5) / 2^3$$

案例二:

1
2
int a = -40;
printf("%d\n", a >> 3); // 输出:-5

a >> b结果:$a/2^b$

7.2. 按位逻辑运算符

7.2.1. 按位非

按位非也叫做按位取反。

格式:~a
特点:将 a 的所有二进制位(包括符号位)取反,即 0 变 1,1 变 0。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int c = 0;
printf("%d\n", ~c);
/*
输出:-1
分析:
c: 00000000 00000000 00000000 00000000 = 0(十进制)
~c:11111111 11111111 11111111 11111111 = -1(十进制)
*/

c = 1;
printf("%d\n", ~c);
/*
输出:-2
分析:
c: 00000000 00000000 00000000 00000001 = 1(十进制)
~c:11111111 11111111 11111111 11111110 = -2(十进制)
*/

c = 19;
printf("%d\n", ~c);
/*
输出:-20
分析:
c: 00000000 00000000 00000000 00010011 = 19(十进制)
~c:11111111 11111111 11111111 11101100 = -20(十进制)
*/

c = -20;
printf("%d\n", ~c);
/*
输出:19
分析:
c: 11111111 11111111 11111111 11101100 = -20(十进制)
~c:00000000 00000000 00000000 00010011 = 19(十进制)
*/

~a结果:$-(a+1)$

7.2.2. 按位与

格式:a & b
特点:

  • 只有当 2 个二进制位都为 1 时,运算结果才为 1
  • 只要有 1 个二进制位为 0,运算结果就为 0

示例:

1
2
3
4
5
6
7
8
9
10
int a = 140;
int b = 200;
printf("%d\n", a & b);
/*
输出:136
分析:
a: 00000000 00000000 00000000 10001100
b: 00000000 00000000 00000000 11001000
a & b:00000000 00000000 00000000 10001000
*/

7.2.3. 按位或

格式:a | b
特点:

  • 只要有 1 个二进制位为 1,运算结果就为 1
  • 只有当 2 个二进制位都为 0 时,运算结果才为 0

示例:

1
2
3
4
5
6
7
8
9
10
int a = 140;
int b = 200;
printf("%d\n", a | b);
/*
输出:204
分析:
a: 00000000 00000000 00000000 10001100
b: 00000000 00000000 00000000 11001000
a | b:00000000 00000000 00000000 11001100
*/

7.2.4. 按位异或

格式:a ^ b
特点:

  • 当 2 个二进制位的值不相等时,运算结果为 1
  • 当 2 个二进制位的值相等时,运算结果为 0

运算顺序:

  • a ^ b == b ^ a
  • (a ^ b) ^ c == a ^ (b ^ c) == (a ^ c) ^ b

示例:

1
2
3
4
5
6
7
8
9
10
int a = 140;
int b = 200;
printf("%d\n", a ^ b);
/*
输出:68
分析:
a: 00000000 00000000 00000000 10001100
b: 00000000 00000000 00000000 11001000
a ^ b:00000000 00000000 00000000 01000100
*/

经典案例:

  • a ^ 0的结果是 a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int a = 10;
int b = 0;
printf("%d\n", a ^ b);
/*
输出:10
分析:
a: 00000000 00000000 00000000 00001010
b: 00000000 00000000 00000000 00000000
a ^ b:00000000 00000000 00000000 00001010
*/

int a = -10;
int b = 0;
printf("%d\n", a ^ b);
/*
输出:-10
分析:
a: 11111111 11111111 11111111 11110110
b: 00000000 00000000 00000000 00000000
a ^ b:11111111 11111111 11111111 11110110
*/
  • a ^ a的结果是 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int a = -10;
int b = -10;
printf("%d\n", a ^ b);
/*
输出:0
分析:
a: 11111111 11111111 11111111 11110110
b: 11111111 11111111 11111111 11110110
a ^ b:00000000 00000000 00000000 00000000
*/

int a = 10;
int b = 10;
printf("%d\n", a ^ b);
/*
输出:0
分析:
a: 00000000 00000000 00000000 00001010
b: 00000000 00000000 00000000 00001010
a ^ b:00000000 00000000 00000000 00000000
*/

7.2.5. 场景练习

在开发中,尽量使用位运算取代乘除模运算,因为位运算的效率比他们高很多。

场景一:用a & 1判断a的奇偶性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 方法一:利用对2取余结果为0或1进行判断,0为偶数,1为奇数
int a = 0;
scanf("%d", &a);
printf("%s\n", (a % 2) ? "奇数" : "偶数");
/*
输入:10
输出:偶数

输入:87
输出:奇数
*/

// 方法二:利用按位与,判断结果是否都为1,如果最后一位是0,则是偶数,结果为0;如果最后一位是1,则是奇数,结果为1。
int a = 0;
scanf("%d", &a);
printf("%s\n", (a ^ 1) ? "奇数" : "偶数");
/*
输入:10
输出:偶数

输入:87
输出:奇数
*/

场景二:用a << b取代a * 2^b(2的b次方)

1
2
3
4
5
6
7
// 编译器可能会警告,加减乘除模的运算级别优先于位移运算
int a = 20;
printf("%d\n", a << 2 + 6); // 输出:5120

// 对位移操作加上小括号提高运算级别
int b = 20;
printf("%d\n", (b << 2) + 6); // 输出:86

场景三:用a >> b取代a / 2^b(2的b次方)

1
2
int a = 25;
printf("%d\n", (a >> 2) + 6); // 输出:12

场景四:不使用第三方变量交换 2 个整形变量的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
int a = 10;
int b = 20;

// 方法一:使用加减法
a = a + b;
b = a - b;
a = a - b;
printf("a:%d b:%d\n", a, b);
/*
输出:a:20 b:10
分析:
a = 10;
b = 20;
a = a + b;
b = a - b = (a + b) - b = a + (b - b) = a + 0 = a = 10;
a = a - b = (a + b) - a = a - a + b = 0 + b = 20;
*/

// 方法二:使用减加法
a = 10;
b = 20;
a = a - b;
b = a + b;
a = b - a;
printf("a:%d b:%d\n", a, b);
/*
输出:a:20 b:10
分析:
a = 10;
b = 20;
a = a - b;
b = a + b = (a - b) + b = a - (b - b) = a - 0 = a = 10;
a = b - a = a - (a - b) = a - a + b = 0 + b = b = 20;
*/

// 方法三:使用积除法
a = 10;
b = 20;
a = a * b;
b = a / b;
a = a / b;
printf("a:%d b:%d\n", a, b);
/*
输出:a:20 b:10
分析:
a = 10;
b = 20;
a = a * b;
b = a / b = (a * b) / b = a * (b / b) = a * 1 = a = 10;
a = a / b = (a * b) / a = (a / a) * b = 1 * b = b = 20;
*/

// 方法四:使用异或
a = 10;
b = 20;
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a:%d b:%d\n", a, b);
/*
输出:a:20 b:10
分析:
a = 10; 0b00000000 00000000 00000000 00001010
b = 20; 0b00000000 00000000 00000000 00010100
a = a ^ b
0b00000000 00000000 00000000 00001010
^ 0b00000000 00000000 00000000 00010100
---------------------------------------------
0b00000000 00000000 00000000 00011110(30)

b = a ^ b = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a
0b00000000 00000000 00000000 00011110
^ 0b00000000 00000000 00000000 00010100
------------------------------------------------
0b00000000 00000000 00000000 00001010(10)

a = a ^ b = (a ^ b) ^ a = (a ^ a) ^ b = 0 ^ b = b
0b00000000 00000000 00000000 00011110
^ 0b00000000 00000000 00000000 00001010
------------------------------------------------
0b00000000 00000000 00000000 00010100(20)
*/

思考:上面由先加后减方法推出了先减后加,能否根据先乘后除推出先除后乘呢?

答案是否定的,因为两数相除的话会影响数据的精度(a / b 是取整操作)。