(图解)解析链表中的LinkList *L, LinkList L和LinkList &L,结构体指针和指向结构体指针的指针的区别

       在计算机中,程序运行时,数据要被存储到内存中,而每个内存都有一个内存地址,如下图:

         内存地址为1001存储着数值1,内存地址为1002存储着数值2.(这里内存地址只是为了方便而这样写,计算机的内存地址表示不一定这样)

        然后咱们看链表的存储结构:

typedef struct Node
{
	int data;
	struct Node* next;
}LNode, *LinkList;

// LNode为结构体,LinkList为结构体指针

其中LNode表示结构体,LinkList表示结构体指针。

        再看传入结构体指针LinkList L 的 DontChange_point()函数:

void DontChange_point(LinkList L)
{
	printf("函数DontChange_point中L指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
	printf("存储L的地址为:%pn", &L);
	L = (LinkList)malloc(sizeof(LNode));
	L->data = 2;
	L->next = NULL;
	printf("现在L指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
	printf("存储L的地址为:%pn", &L);
	return;
}

        再看传入指向结构体指针的指针LinkList *L(二级指针)的Change_point()函数:

void Change_point(LinkList* L)
{
	printf("L指向的地址为:%pn", L);
	printf("存储L的地址为:%pn", &L);
	*L = (LinkList)malloc(sizeof(LNode));
	(*L)->data = 2;
	(*L)->next = NULL;
	return;
}

那传入结构体的指针和指向结构体指针的指针区别呢?

先看下图:

        首先咱们将结构体存储在1(内存地址为1001)处,而指向结构体的指针 L 保存的便是结构体的地址1001(即解引此地址(*L)可以得到结构体),而这个结构体指针需要内存来存储它,故其也有一个地址(即内存地址2001保存了L,L保存了地址2001上的数据(即1001),解引此地址(2001)可以得到内存地址1001,再解引1001便可得到结构体,双重解引),即内存地址2001保存着内存地址1001(也就是L),而内存地址1001保存着结构体。

        可能这有点迷糊,咱们可以这样理解就是咱们可以把内存地址想成一个盒子,将结构体想成是一件物品,编号为1001(相当于内存地址1001)的盒子(即L)装着这个物品(即结构体),如果我们将这个盒子打开就可以得到这个物品(结构体),其实这个过程相当于解引结构体指针(指针解引),然后咱们再把编号为2001的盒子把编号为1001的盒子(即L)装起来,咱们是不是要打开两次盒子(一次为2001,一次为1001)才能得到这个物品(相当于双重解引)。


        传入结构体指针,相当于把内存地址1001赋给了DontChange_point函数中的 L(传址),但 L不一定需要指向这块内存地址(1001),其也可以指向别的内存地址,当其指向别的地址,然后再进行操作,会不会影响我们之前传入的内存1001里的结构体呢?如:

void DontChange_point(LinkList L)
{
	printf("函数DontChange_point中L指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
	printf("存储L的地址为:%pn", &L);
	L = (LinkList)malloc(sizeof(LNode));
	L->data = 2;
	L->next = NULL;
	printf("现在L指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
	printf("存储L的地址为:%pn", &L);
	return;
}

        这个指针L指向了用malloc()函数分配的内存,并且进行了修改L指向的结构体的操作,但它会影响我们之前传入的地址所指向的结构体吗?咱们用程序说话:

int main(void)
{
	LinkList L;
	L = (LinkList)malloc(sizeof(LNode));
	L->data = 1;
	L->next = NULL;
	printf("L指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
    printf("存储L的地址为:%pn", &L);
	DontChange_point(L);
	printf("现在main()函数中L指向的data为:%dn", L->data);
    return 0;
}

程序输出:

      在main()函数结构体指针L指向的地址和刚传入DontChange_point()函数的地址L是一致的,但保存这个结构体指针的地址确是不一致的,说明是两个变量(main函数里的L和DontChange_point()函数的L)是不一样的。故后面改变DontChange_point()函数的L的指向,影响不到main()函数里的L,故在DontChange_point()函数的L对结构体的操作影响不了main()函数中L指向的结构体,而事实也证明如此,两次输出都是1,并没有因为DontChange_point()函数的L的操作而改变为结构体的data由1变为2.

如下图:

刚开始,DontChange_point()函数的L的指向


 后面DontChange_point()函数的L经过malloc()函数进行分配:


        而传入指向结构体指针的指针,相当于把main()函数中保存L的地址给传进去了,然后再这块地址上做的操作会不会影响外面main()函数中的L呢?咱们直接上程序:

void Change_point(LinkList* L)
{
	printf("L指向的地址为:%pn", L);
	printf("存储L的地址为:%pn", &L);
	*L = (LinkList)malloc(sizeof(LNode));
	(*L)->data = 2;
	(*L)->next = NULL;
	return;
}
int main(void)
{
	LinkList L;
	L = (LinkList)malloc(sizeof(LNode));
	L->data = 1;
	L->next = NULL;
	printf("L指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
	printf("存储L的地址为:%pn", &L);
	Change_point(&L); // 获取保存L的地址
	printf("现在main()函数中L指向的data为:%dn", L->data);
	return 0;
}

         咱们在Change_point()函数中,将传入保存mian()函数中L的地址赋给了Change_point()函数L,再对这块内存要保存的数据进行修改(修改这块内存要保存的地址,利用malloc()函数进行分配),即这块内存上的数据要改变,而main()函数的L指向的是这块内存的数据,故其也跟着改变,那事实是不是这样呢,咱们看程序输出。

程序输出:

程序输出说明,在Change_point()函数的操作影响到了外面main()函数的L,并且Change_point()函数中的L和外面main()函数保存结构体指针L的地址是一致的,说明对Change_point()函数中L的地址上所改变的内容会影响到main()函数的L(因为main()函数中的L是保存这块内存的数据信息)。程序输出也证明了事实如此,在没执行函数前,L->data = 1,指行函数后L->data = 2。

 如下图:

刚开始:(下面的L均代表的是main()函数中的L)

 利用malloc()函数进行操作后:


 LinkList &L是C++的指针引用,其相当于LinkList *L,分析也类似分析LinkList *L,这里就不介绍了。

总的来说:

        在参数为结构体指针的函数里,你可以对这个结构体指针所指向的内容进行修改,但函数外的结构体指针一直会指向这块内存(不会因为函数参数所指向别处,而跟着指向别处)。也就是说如果函数的参数(结构体指针)没有改变指向(指向别处),函数对指针所指向的内容进行修改,函数外的结构体指针(传入函数的指针)所指向的内容也会改变,但若函数的参数改变指向(即不指向传入指针指向的内存地址,其修改的内容不会影响到函数外结构体指针所指向的内容,即函数外结构体指针之前指向的内容不会改变)。参数为结构体指针的函数,不会修改函数外结构体指针所指向的内存地址,但可以修改结构体指针指向的结构体。

        在参数为指向结构体指针的指针,会影响到函数外的结构体指针所指向的地址,进而影响结构体指针指向的结构体。

        所以当我们的函数不需要修改函数外结构体指针所指向的内存地址,同时又想修改结构体指针所指向的结构体,便可以将函数的参数设为结构体指针。但如果我们的函数想修改函数外结构体指针所指向的内存地址时,我们便要传入指向结构体指针的指针,不可传入结构体指针。


        内容到此结束,谢谢大家的阅读!!

测试程序:

#include <stdio.h>
#include <stdlib.h>

typedef struct Node
{
	int data;
	struct Node* next;
}LNode, *LinkList;

void Change_point(LinkList* p);
void DontChange_point(LinkList p);

int main(void)
{
	LinkList L;
	L = (LinkList)malloc(sizeof(LNode));
	L->data = 1;
	L->next = NULL;
	printf("L指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
	printf("存储L的地址为:%pn", &L);
	DontChange_point(L);
	printf("现在main()函数中L指向的data为:%dn", L->data);
	Change_point(&L);
	printf("现在main()函数中L指向的data为:%dn", L->data);
	return 0;
}

void Change_point(LinkList* L)
{
	printf("L指向的地址为:%pn", L);
	printf("存储L的地址为:%pn", &L);
	*L = (LinkList)malloc(sizeof(LNode));
	(*L)->data = 2;
	(*L)->next = NULL;
	return;
}

void DontChange_point(LinkList L)
{
	printf("函数DontChange_point中L(没改变其指向)指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
	printf("存储L的地址为:%pn", &L);
	L = (LinkList)malloc(sizeof(LNode));
	L->data = 2;
	L->next = NULL;
	printf("现在L(改变了其指向)指向的地址为:%pn", L);
	printf("L指向的data为:%dn", L->data);
	printf("存储L的地址为:%pn", &L);
	return;
}