3.3 赋值运算符
1. 直接赋值
很简单,= 左侧写要赋值的对象,右侧写要赋予的值。比如:
int a = 0; // 在定义对象 a 的同时,将 0 赋值给 a
a = 5; // 把 5 这个值给 a 这个对象2
2. 复合赋值
复合赋值运算符由一个二元运算符和 = 组成,如 +=、<<=,可以把运算和赋值写在一起,使代码更简洁。
2.1 加法复合赋值 +=
a += b; 等效于 a = a + b;,把 a 与 b 的和存入 a。
int a = 10;
a += 5; // 等效于 a = a + 5;
// 此时 a 的值为 152
3
2.2 减法复合赋值 -=
a -= b; 等效于 a = a - b;,把 a 与 b 的差存入 a。
int a = 10;
a -= 5; // 等效于 a = a - 5;
// 此时 a 的值为 52
3
2.3 乘法复合赋值 *=
a *= b; 等效于 a = a * b;,把 a 与 b 的积存入 a。
int a = 10;
a *= 5; // 等效于 a = a * 5;
// 此时 a 的值为 502
3
2.4 除法复合赋值 /=
a /= b; 等效于 a = a / b;,把 a 除以 b 的商存入 a。
int a = 10;
a /= 5; // 等效于 a = a / 5;
// 此时 a 的值为 22
3
两个操作数都是整数时,进行的是整数除法,小数部分被舍去。
2.5 取模复合赋值 %=
a %= b; 等效于 a = a % b;,把 a 除以 b 的余数存入 a。
int a = 10;
a %= 3; // 等效于 a = a % 3;
// 此时 a 的值为 12
3
2.6 位运算复合赋值
位运算符也可以与 = 组成复合赋值运算符:
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;(右移)。
int n = 10; // 二进制为 1010
int MASK = 12; // 二进制为 1100
n &= MASK; // 执行按位与运算,结果为 1000(十进制为 8)
// 此时 n 的值为 82
3
4
3. 习题
3.1 巩固
写出下面程序的输出。不要运行,先在每一步赋值之后记下相关对象的值。
#include <stdio.h>
int main(void) {
int a = 17;
a += 5;
a /= 4;
a %= 3;
a <<= 3;
a ^= 6;
printf("%d\n", a);
int b = 2;
b *= 3 + 4;
b <<= 1 + 1;
printf("%d\n", b);
int c = -7;
c %= 3;
printf("%d\n", c);
int d = 0, e = 0;
d = e = 4;
printf("%d %d\n", d, e);
int f = 0;
printf("%d\n", f = 7);
printf("%d\n", f);
return 0;
}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
编译运行核对,再回答:
b *= 3 + 4右侧的加法为什么不会被拆成(b *= 3) + 4?d = e = 4按什么顺序结合?改写成d = (e = 4)后结果是否相同?结合性在这里起什么作用?printf的实参位置直接写f = 7为什么可以?这个赋值表达式的值是什么?
下面两个函数本意是累加四个数、把 total 减去 3,运行结果却是 9 和 -3:
#include <stdio.h>
int sum_four(int a, int b, int c, int d) {
int sum = 0;
sum =+ a;
sum =+ b;
sum =+ c;
sum =+ d;
return sum;
}
int main(void) {
printf("%d\n", sum_four(4, 7, 1, 9));
int total = 10;
total =- 3;
printf("%d\n", total);
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 说明
=+和=-各自被解析成了什么运算,算出上面两个结果。 - 用
gcc -std=c17 -Wall -Wextra编译,把诊断信息抄下来。为什么这个错误一条警告都没有? - 把
sum =+ a改成sum = +a、把total =- 3改成total = -3(只调空格),能否表达本意?说明原因。 x =- 3与x -= 3的作用不同,举一个x的初值使两者结果不同,并各写出结果。
3.2 编程
写一个函数,把非负的总秒数拆成时、分、秒:
void mdr_hms(int total_seconds);要求:
- 拆分过程只使用
/=和%=,不写total_seconds / 60这样的普通除法表达式。 - 形参
total_seconds不改动,另用一个局部对象保存剩余秒数,每一步都把结果写回这个对象。 - 输出固定为
HH:MM:SS,不足两位补零。
验收:依次传入 3725、59、3600、86399,输出应为
01:02:05
00:00:59
01:00:00
23:59:592
3
4
完成后把两处 remain /= 60 都改成 remain %= 60,其余不动,记下新的输出,并说明为什么三个字段都变成了同一个数。
3.3 综合与挑战
(1) [3.2] 下面的函数想先把第 5 位清零、再把第 3 位置 1,实际返回 8:
#include <stdint.h>
uint32_t mdr_config(void) {
uint32_t ctrl = 0x25;
ctrl &= ~1u << 5;
ctrl |= 1u << 3;
return ctrl;
}2
3
4
5
6
7
8
写出 0x25 的二进制,按运算符优先级算出 ctrl &= ~1u << 5 实际做了什么,再改成正确的写法并验证返回 0x0D。
(2) [3.1] 下面两段累加的结果不同:
#include <stdint.h>
uint8_t a = 0;
a += 0xF0;
a += 0x30;
printf("%u\n", a);2
3
4
5
6
unsigned b = 0;
b += 0xF0;
b += 0x30;
printf("%u\n", b);2
3
4
写出两段的输出。再写出 a = a + 0xF0; a = a + 0x30; 的结果,说明两种写法在类型转换上有没有区别。
(3) [3.3] 写出下面两行的结果,并解释原因:
int d = 0, e = 0;
d += e += 4;2
3
再把第一行的赋值改成 (d = e) = 4;,记录编译错误原文,说明赋值表达式为什么不能放在赋值号左边。
(4) [4.2] 复合赋值不是 a = a op b 的纯文本替换。写出下面四行的输出,并说明每行右侧的运算结果在存入左侧对象之前经历了什么:
int i = 10;
i /= 4.0;
int k = 7;
k *= 1.5;
unsigned u = 10;
u -= 20;
int n = 10;
n += 2.9;2
3
4
5
6
7
8
9
10
11
最后回答:标准规定 E1 op= E2 与 E1 = E1 op E2 只有一处不同,这一处不同在什么情况下才看得出来?