Pointer to pointer
前面的课程中我们已经知道了如何在一些基本场景中使用指针. 但是有些场景可能指针的使用会给我们带来困惑, 其中一个场景就是指向指针的指针. 本节将会用一个示例观察怎样使用指向指针的指针.
example
假设下图是计算机内存的逻辑视图, 水平展示, 地址从左向右增大.
我们定义一些变量, 并假设它们位于内存中的地址. 具体代码:
int x = 5; // address of x is 225
int *p = &x; //address of p is 215
int **q = &p;//address of q is 205
int ***r = &q;//address of r is 230
-
q
: 一个指针指向变量p
,p
是一个指向整型的指针. 为了存储一个类型为int *
的变量的地址, 也就是我们需要一个指向指针的指针, 我们需要在p
类型的基础上再放一个代表指针的*
:int **q
. -
r
: 更进一步, 为存放类型为int **
类型变量的地址, 我们在此基础上叕加了一个*
. -
上述声明类似递归过程. 如果需要存储类型为
type
的变量的地址, 我们的指针类型需要为type *
.
code
用一个C
代码来展示上述指针:
int x = 5;
int *p = &x;
int **q = &p;
int ***r = &q;
我们会打印一些变量, 你可以思考这些变量的打印结果. 不妨先假设上述变量分别被分配了上图所示的地址.
printf("%d\n", *p);
printf("%d\n", *q);
printf("%d\n", *(*q) );
printf("%d\n", *(*r) );
printf("%d\n",*(*(*r)) );
输出结果:
-
p
指向内存的值:5
. -
q
指向内存的值:225
. -
解引用两次:
*q
的结果是225
(p
的值), 地址225
的内存且类型是$int$的值是5
.这里也可以不加括号:
**q
, 但尽可能使用括号可以避免不同操作优先级不同导致程序执行我们预料之外的行为. -
解引用两次,
*r
也就是q
的值:205
; 进一步地址是205
且类型为int *
的变量的值为215
. -
在
4.
的基础上再一次解引用把我们带到了x
的值:5
.从
r
开始, 每次解引用在上图中都相当于按箭头指示向前走了一步, 获取箭头指向的值.
让我们对代码做些修改并运行它:
int x = 5;
int *p = &x;
int **q = &p;
int ***r = &q;
printf("value of x is %p\n", *p);
printf("address of x is %p\n", p);
printf("*q = %p\n", *q); //
printf("*(*q) = %p\n", *(*q) );
printf("*(*r) = %p\n", *(*r) );
printf("*(*(*r)) = %p\n",*(*(*r)) );
***r = 9;
printf("value of x is %p\n", *p);
正如我们从代码中看到的, 我们可以通过变量r
的解引用链(chain of deferencing
)来修改x
的值.
如果我们指向**q = *p + 2
, 实际上运行结果等价于x = x + 2
.