本文是上一篇《从内存与汇编的角度理解C语言指针第01篇:p、*p、&p的区别》的续篇。
一、C程序
现有C程序如下:
int main(void) {
int a = 1; // 定义变量 a,值为 1
int *p = &a; // 定义指针 p,指向 a
int **pp = &p; // 定义指针 pp,指向 p
return 0;
}
二、对应的汇编代码
以上C程序中的第2~4行对应的汇编代码如下:
// 对应C代码:int a = 1;
mov DWORD PTR [rbp-0xc],0x1 // 把数字1存放到内存单元[rbx-0xc]处,占4个字节
// 对应C代码:int *p = &a;
lea rax,[rbp-0xc] // 把内存单元[rbx-0xc]的地址存入rax寄存器
mov QWORD PTR [rbp-0x18],rax // 把rax寄存器中的值存放到内存单元[rbp-0x18]处,占8个字节
; 对应C代码:int **pp = &p;
lea rax,[rbp-0x18] // 把内存单元[rbp-0x18]的地址存入rax寄存器
mov QWORD PTR [rbp-0x8],rax // 把rax寄存器中的值存放到内存单元[rbp-0x8]处,占8个字节
三、内存布局
内存布局如下:
变量 | 变量地址 | 内存地址 | 内存值 |
---|---|---|---|
pp | &pp | 0x61fe18 | 0x61fe08 |
p | &p | 0x61fe08 | 0x61fe14 |
a | &a | 0x61fe14 | 0x01 |
四、pp
、*pp
、&pp
、**pp
的区别
在本例中:
a
:变量a
,值是1p
:指针变量p
,值是变量a
的地址0x61fe14
*p
:指针变量p
的所指变量a
的值,是1&p
:指针变量p
的地址0x61fe08
pp
:指针变量pp
,值是指针变量p
的地址0x61fe08
*pp
:指针变量pp
的所指变量p
的值,而p
的值在上面第2行有说明,即变量a
的地址0x61fe14
。可以看出*pp
==p
。&pp
:指针变量pp
的地址0x61fe18
**pp
:为方便理解,我们加上括号,得到:*(*pp)
。*pp
在第6行已经有说明,*pp
==p
,代入*(*pp)
,则*(*pp)
==*p
,而*p
在第3行有说明,它是指针变量p
的所指变量a
的值,也就是1
五、对于*
的理解
按照《C程序设计语言(K&R)第2版》书中的说明:“一元运算符*
是间接寻址或间接引用运算符。当它作用于指针时,将访问指针所指向的对象。”
在执行*p
时,CPU执行了2步操作:
1. 通过p
的地址去内存中查找,得到了p
的内容,p
的内容又是一个地址(a
的地址)
2. 通过a
的地址去内存中查找,得到a
的值。需要2步操作才取得了数据,因此是间接寻址
。
因此*
可以理解为:CPU通过间接寻址去取值。
这里需要说明,间接寻址取值,取到什么就是什么,这个值是地址
还是数据
是由我们程序员来决定的。比如对于**pp
来说,第1步得到的是地址,第2步得到的也是地址,第3步得到的才是数据。
参考资料:寄存器直接寻址和间接寻址的区别?