高级指针话题
本章介绍了各种各样的涉及指针的技巧。有些技巧非常实用,另外一些技巧则学术味更浓一些,还有一些纯属找乐。但是,这些技巧很好地说明了这门语言的各种原则。
13.1 进一步探讨指向指针的指针
上一张使用了指向指针的指针,用于简化向单链表插入新值的函数。另外还存在很多领域,指向指针的指针可以在其中发挥重要的作用。
这里有一个通用的例子:
int i;
int *pi;
int **ppi;
如果它们是自动变量,则无法猜测它们的初始值。
请问下面各条语句的效果是什么呢?
1. printf( "%d\n", ppi );
2. printf( "%d\n", &ppi );
3. *ppi = 5;
1如果ppi是个自动变量,它就未被初始化,这条语句将打印一个随机值。如果它是个静态变量,这条语句将打印0。
2这条语句将存储ppi的地址作为十进制整数打印出来。这个值并不是很有用。
3这条语句的结果是不可预测的。对ppi不应该指向间接访问操作,因为它尚未初始化。接下来的两条语句用处比较大。
ppi = π
这条语句把ppi初始化为指向变量pi。以后就可以对ppi执行间接访问操作了。
*ppi = &i;
这条语句把pi(通过间接访问)初始化为指向变量i。指向完上面最后两条语句之后,这些变量变成了下面这个样子:
现在,下面各条语句具有相同的效果:
i = 'a';
*pi = 'a';
**ppi = 'a';
在一条简单的对i赋值的语句就可以完成任务的情况下,为什么还要使用更为复杂的涉及间接访问的方法呢?这是因为简单赋值并不总是可行,例如链表的插入。在那些函数中,无法使用简单赋值,因为变量名在函数的作用域内部是未知的。函数所拥有的的只是一个指向需要修改的内存位置的指针,所以要对该指针进行间接访问操作以访问需要修改的变量。
在前一个例子中,变量i是一个整数,pi是一个指向整数的指针。但ppi是一个指向pi的指针,所以它是一个指向整型的指针的指针。假定我们需要另一个变量,它需要指向ppi。那么,它的类型当然是“指向整型的指针的指针的指针”,而且它应该像下面这样声明:
int ***pppi;
间接访问的层次越多,需要用到它的次数就越少。但是,一旦真正理解了间接访问,无论出现多少层间接访问,应该都能十分轻松地应付。
提示:
只有当确实需要时,才应该使用多层间接访问。不然的话,程序将会变得更庞大、更缓慢并且更难于维护。
/*
** 指向指针的指针。
*/
#include <stdio.h>
#include <stdlib.h>
int g_i;
int *g_pi;
int **g_ppi;
int main( void ){
int i;
/* pi is a pointer points to an int */
int *pi;
/* ppi is a pointer points to a pointer */
int **ppi;
/*
** the result of operation is unpredictable.
** Because the ppi doesn't be initialized.
** *ppi = 5;
*/
printf( "g_i = %d, i = %d\n", g_i, i );
printf( "g_pi = %p, g_pi = %d, pi = %p, pi = %d\n", g_pi, g_pi, pi, pi );
printf( "g_ppi = %p, g_ppi = %d, ppi = %p, ppi = %d\n", g_ppi, g_ppi, ppi, ppi );
/* ppi is a pointer points to pi */
ppi = π
printf( "after ppi = &pi, ppi = %p, &pi = %p, *ppi = %p, pi = %p\n", ppi, &pi, *ppi, pi );
/* *ppi is a pointer points to i */
*ppi = &i;
i = 'a';
printf( "after *ppi = &i and i = 'a', *ppi = %p, pi = %p, &i = %p\n**ppi = %c, *pi = %c, i = %c\n",
*ppi, pi, &i, **ppi, *pi, i );
int ***pppi = &ppi;
printf( "after pppi = &ppi, pppi = %p, &ppi = %p, *pppi = %p, ppi = %p\n", pppi, &ppi, *pppi, ppi );
return EXIT_SUCCESS;
}
/* 输出:

*/