首先,我们看一下书中初始化链表的代码:
#include<stdio.h>
#define TSIZE 45
typedef struct film {
char title[TSIZE];
int rating;
} Item;//储存电影信息的结构
typedef struct node {
Item item;
struct node * next;//指向下一结构的指针
} Node;//node结构构成了一个链表的节点
typedef Node * List;
void InitializeList(List * plist);
int main(void)
{
List movies;//movies本身为一个指针,该指针指向一个ndoe结构
printf("%p\n", movies);//后添加
InitializeList(&movies);//传入指向链表的指针,初始化链表为空
printf("%p\n", movies);//后添加
return 0;
}
void InitializeList(List * plist)//初始化链表函数
{
*plist = NULL;
printf("%p\n", *plist);//后添加
}
为了方便显示程序结果,我在程序中加入了三条printf语句,分别显示初始化前、初始化函数中、回到主调函数中movies的值,程序运行结果如下:
可以看到,程序正常运行,并且movies的值已被初始化为NULL。
那么,如果我们将上面的语句更改为:
void InitializeList(List plist)//注意,函数原型也要更改
{
plist = NULL;
printf("%p\n", plist);
}
重新调用该函数InitializeList(movies),也就是说我们传递给该函数的参数为指针本身,而不是指向该指针的指针,再次运行程序,程序的运行结果如下:
可以看到,在初始化函数中,movies的值已被初始化为NULL,但是回到主调函数后,movies的值并没有发生改变,为什么?
我们先将问题简单化,解释指针的原理:
假设int n = 5; int * pn = &n; int ** p_pn = &pn; 它们的关系如下:
从上面的关系图可知:n == 5,&n == CCCCCCCC
*pn == n == 5,pn == &n == CCCCCCCC,&pn == DDDDDDDD
*p_pn ==pn == CCCCCCCC,p_pn == &pn == DDDDDDDD,&p_pn == EEEEEEEE。有点绕,但是好理解。
我们可以将上面程序中的movies看作是pn,因为它们都是一个指针,那么plist就可以看做p_pn,因为它们都是指向指针的指针。
我们都知道,如果需要在一个被调函数中改变一个主调函数的变量,一般采
用传递变量的地址,而不是直接传递变量,即采用change(pn);(调用函数语句,change()为被调函数),而不是采用change(n),因为如果传递的是变量,程序会将该变量备份,在被调函数中改变的只是备份的变量,而不是变量本身,退出被调函数后,该备份变量就会被销毁,这也就是为什么明明在被调函数显示变量已改变,但是回到主调函数后变量又变成原来的值。
同样,如果我们要改变pn,那么就不能传递pn本身,必须传递&pn,即p_pn,这也就是为什么我们在调用函数时传递的是&movies,而不是movies。因为movies指向一个链节,它的值为该链节的地址,如果我们要将它的值变为NULL,那么就要传递它的地址,然后通过解引用运算符*改变它的值。
说到这里,原因我们大概都清楚了,对指针也有了初步了解,但是有一个问题我们需要注意:
我们改变了pn的值,但是&n本身发生了改变吗?答案是并没有,用简单的程序测试一下:
#include<stdio.h>
void change(int ** ptr);
int main(void)
{
int n = 5;
int * pn = &n;
int ** p_pn = &pn;
printf("%p\n", &n);
printf("%p\n", pn);
change(p_pn);
printf("%p\n", &n);
printf("%p\n", pn);
return 0;
}
void change(int ** ptr)
{
*ptr = NULL;
}
运行结果如下:
可以看到pn的值已经被初始化为NULL,但是n本身的地址却并没有改变,因为pn的值以前是n所在的地址,但是改变pn的值后,pn不再指向n,n本身并没有改变,依然存在,其地址当然也不会改变。
由于我也是刚开始学,所以如果有错误,还请指正,谢谢!