看视频解析请点击这里。
一、C程序
int main(void) {
int a = 1;
int *p = &a;
*p = 2;
int **pp = &p;
**pp = 3;
return 0;
}
二、对应的汇编代码
; C代码:int a = 1;
mov DWORD PTR [rbp-0xc],0x1
; C代码:int *p = &a;
lea rax,[rbp-0xc]
mov QWORD PTR [rbp-0x18],rax
; C代码:*p = 2;
mov rax,QWORD PTR [rbp-0x18]
mov DWORD PTR [rax],0x2
; C代码:int **pp = &p;
lea rax,[rbp-0x18]
mov QWORD PTR [rbp-0x8],rax
; C代码:**pp = 3;
mov rax,QWORD PTR [rbp-0x8]
mov rax,QWORD PTR [rax]
mov DWORD PTR [rax],0x3
三、内存布局
1、执行完int a = 1 之后的内存布局
变量 | 变量地址 | 内存地址 | 内存值 |
---|
a | &a | 0x61fe14 | 0x01 |
2、执行完int *p = &a 之后的内存布局
变量 | 变量地址 | 内存地址 | 内存值 |
---|
p | &p | 0x61fe08 | 0x61fe14 | a | &a | 0x61fe14 | 0x01 |
这里,变量p 的内存值是变量a 的内存地址。
3、执行完*p = 2 之后的内存布局
变量 | 变量地址 | 内存地址 | 内存值 |
---|
p | &p | 0x61fe08 | 0x61fe14 | a | &a | 0x61fe14 | 0x02 |
与上一步的区别是,变量a 的内存值从1 变成了2 。
4、执行完int **pp = &p 之后的内存布局
变量 | 变量地址 | 内存地址 | 内存值 |
---|
pp | &pp | 0x61fe18 | 0x61fe08 | p | &p | 0x61fe08 | 0x61fe14 | a | &a | 0x61fe14 | 0x02 |
这里,变量pp 的内存值是变量p 的内存地址,变量p 的内存值是变量a 的内存地址。
5、执行完**pp = 3 之后的内存布局
变量 | 变量地址 | 内存地址 | 内存值 |
---|
pp | &pp | 0x61fe18 | 0x61fe08 | p | &p | 0x61fe08 | 0x61fe14 | a | &a | 0x61fe14 | 0x03 |
与上一步的区别是,变量a 的内存值从2 变成了3 。
四、执行*p = 2 时CPU干了些什么
准确来说,干的就是上面的汇编代码里面做的事情:
mov rax,QWORD PTR [rbp-0x18]
mov DWORD PTR [rax],0x2
与内存布局对应着来理解就是:
- CPU已经知道变量
p 的内存地址是0x61fe08 ,于是去把它的内存值取出来,这个值是0x61fe14 ,把这个值放到了rax 寄存器中。 - CPU发现
rax 寄存器中的值也是一个内存地址,接下来就把数字2 存放到了这个地址所在的内存单元处。
那CPU是怎么知道内存单元或寄存器中的值是内存地址的呢?这是我们汇编指令告诉它的。 [rbp-0x18] 和[rax] 中的那个[] 的作用就是:访问括号里面的内存地址。 也就是说,指令里面有[] ,CPU就知道是内存地址了。
五、执行**pp = 3 时CPU干了些什么
汇编代码:
mov rax,QWORD PTR [rbp-0x8]
mov rax,QWORD PTR [rax]
mov DWORD PTR [rax],0x3
与内存布局对应着来理解就是:
- CPU已经知道变量
pp 的内存地址是0x61fe18 ,于是去把它的内存值取出来,这个值是0x61fe08 ,把这个值放到了rax 寄存器中。 - CPU发现这时
rax 寄存器中的值也是一个内存地址0x61fe08 ,于是去把这个地址对应的内存值取出来,这个值是0x61fe14 ,这个值仍然存放到rax 寄存器中。 - CPU发现
rax 寄存器中的值还是一个内存地址,接下来就把数字3 存放到了这个地址所在的内存单元处。
六、总结
在CPU眼中是没有变量一说的,它不知道a 、p 、pp ,它只知道指令,比如:
指令让它从内存里取值放到寄存器; 指令让它把寄存器中的值当成一个地址,然后去另一块内存区域里取值,还是放到寄存器里面; 指令让它把数字3 存放到内存区域里面。
|