|
看视频解析请点击这里。
一、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存放到内存区域里面。
|