a++ 和 ++a

先来看几个问题

问题1

1
2
3
int a = 3;
int b = a++ + a++;
b是多少?

问题2

1
2
3
int a = 3;
int b = a++ + ++a;
b是多少?

问题3

1
2
3
int a = 3;
int b = ++a + a++;
b是多少?

问题4

1
2
3
int a = 3;
int b = ++a + ++a;
b是多少?

学习EAX、EBX、ECX和EDX寄存器的用途
(1)EAX寄存器:EAX称为累加器,常用于算数运算、布尔操作、逻辑操作、返回函数结果等。
(2)EBX寄存器:EBX称为基址寄存器,常用于存档内存地址。
(3)ECX寄存器:ECX称为计数寄存器,常用于存放循环语句的循环次数,字符串操作中也常用。
(4)EDX寄存器:称为数据寄存器,常常和EAX一起使用。

问题1

int a = 3;
int b = a++ + a++;

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
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp 开辟变量空间
movl $3, -4(%rbp) 将3赋值给变量a=3
movl -4(%rbp), %edx 将变量赋值给寄存器edx = 3 先使用,加法运算变量赋值
leal 1(%rdx), %eax 将edx加1赋值给eax =4, edx=3 再加加
movl %eax, -4(%rbp) 将eax赋值给变量,此时a完成了++,a = 4 再加加
movl -4(%rbp), %eax 将变量赋值给了eax = 4, 先使用,加法运算变量赋值
leal 1(%rax), %ecx 将eax+1赋值给了ecx=5, eax=4 再加加
movl %ecx, -4(%rbp) 将ecx赋值给了变量,此时a完成了第二个++,a=5 再加加
addl %edx, %eax 将edx=3加上eax=4等于7,然后赋值给eax 执行加法运算
movl %eax, -8(%rbp) 将eax=7赋值给变量b=7
movl -8(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc

结果输出 7

问题2

int a = 3;
int b = a++ + ++a;

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
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp 开辟变量空间
movl $3, -4(%rbp) 将3赋值给变量a=3
movl -4(%rbp), %eax 将变量赋值给寄存器eax = 3 先使用,加法运算变量赋值
leal 1(%rax), %edx 将eax加1赋值给edx =4, eax=3 再加加
movl %edx, -4(%rbp) 将edx赋值给变量,此时a完成了++,a = 4 再加加
addl $1, -4(%rbp) 将变量加1然后赋值给变量 ++a完成了,a = 5 先加加
movl -4(%rbp), %edx 将变量赋值给edx=5 再使用,加法运算变量赋值
addl %edx, %eax 将edx=5加上eax=3后赋值给eax=8 执行加法运算
movl %eax, -8(%rbp) 将eax=8赋值给了变量b=8
movl -8(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc

结果输出 8

问题3

int a = 3;
int b = ++a + a++;

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
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp 开辟变量空间
movl $3, -4(%rbp) 将3赋值给变量a=3
addl $1, -4(%rbp) 将变量加1然后赋值给变量 ++a完成了,a = 4 先加加
movl -4(%rbp), %eax 将变量赋值给了eax=4, 再使用,加法运算变量赋值
leal 1(%rax), %edx 将eax=4加1赋值给edx=5 该步难以理解
movl %edx, -4(%rbp) 将edx赋值给了变量a,a = 5 该步难以理解
movl -4(%rbp), %edx 将变量a=5,赋值回了edx =5 加法运算变量赋值
addl %edx, %eax 将edx=5加上eax=4赋值给eax=9 执行加法运算
movl %eax, -8(%rbp) 将eax=9赋值给了变量b=9
movl -8(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc

结果输出9

为什么会得到 9?
这里的关键是编译器在处理 ++a + a++ 时,将 a++ 计算成了 5 而不是 4。我们通常期望 a++ 返回自增前的值,也就是 4,但在你的汇编代码中,编译器似乎在计算右边的 a++ 时直接使用了 a 当前自增后的值 5。
原因总结:
C 语言的表达式求值顺序未定义,在这种复杂表达式(尤其包含自增、自减的情况)中,编译器的行为可以有所不同。不同的编译器,甚至同一编译器在不同的优化设置下,可能会产生不同的结果。
在你的汇编代码中,编译器在计算 ++a + a++ 时,似乎使用了 a 在自增后的值 5,而不是预期中的自增前值 4。
结论:
你的编译器处理 ++a + a++ 时返回了 9,因为在这个实现中,a++ 计算为了 5 而不是预期的 4。这与编译器的优化和求值顺序有关。

问题4

int a = 3;
int b = ++a + ++a;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp 开辟变量空间
movl $3, -4(%rbp) 将3赋值给变量a=3
addl $1, -4(%rbp) 将变量加1然后赋值给变量 ++a完成了,a = 4 先加加
addl $1, -4(%rbp) 将变量加1然后赋值给变量 ++a又完成了,a = 5 先加加
movl -4(%rbp), %eax 将变量a=5赋值给了eax=5 加法计算赋值
addl %eax, %eax 将eax=5加上eax=5赋值给eax=10 加法运算
movl %eax, -8(%rbp) 将eax=10赋值给了变量b=10
movl -8(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc

结果输出10

5 补充问题

int a = 3;
int b = ++a;
int c = a++;
printf(“%d,%d”, b, c);

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
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp 开辟变量空间
movl $3, -4(%rbp) 将3赋值给变量a=3
addl $1, -4(%rbp) 将变量加1然后赋值给变量 ++a完成了,a = 4 先加加
movl -4(%rbp), %eax 将变量赋值给寄存器eax=4,再使用
movl %eax, -8(%rbp) 将eax=4赋值给变量b=4
movl -4(%rbp), %eax 将变量赋值给寄存器eax=4,先使用
leal 1(%rax), %edx 将eax=4加1赋值给edx=5, 再加加
movl %edx, -4(%rbp) 将edx=5赋值给变量a=5 再加加
movl %eax, -12(%rbp) 将eax=4赋值给变量c=4
movl -12(%rbp), %edx
movl -8(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc

输出4,4

总结

顺序 结果 解释 备注
int b = a++ + a++; 7 3+4 比较好理解
int b = a++ + ++a; 8 3+5 比较好理解
int b = ++a + a++; 9 4+5 很难理解,理论上是4+4,可能是编译器未知行为
int b = ++a + ++a; 10 5+5 比较难理解,理论上是4+5,可能是编译器未知行为