链表
一个疑问
-
今天在复习链表时遇见了几个问题,其实是拖了很久的问题,现在尝试分析一下
结构体: typedef struct Node{ int data; struct Node *next; }Node;
main() Node *list=NULL; CreatNode(list); printf("%d",list->data); Node * CreatNode(Node *head) { Node * pNew=(Node *)malloc(sizeof(Node)); pNew->data=0; pNew->next=NULL; head=pNew; }
在主函数里,定义了一个Node类型的指针并赋值NULL,然后调用CreatNode函数试图用一个新的节点来对它赋值,以完成对第一个节点的初始化操作。
本以为会输出0,但结果却是程序崩溃了,为何?
先把这个问题放一边,来看另一段代码:
main() { Node *list=NULL; list=CreatNode() } Node * CreatNode() { Node * head=(Node *)malloc(sizeof(Node)); head->data=0; head->next=NULL; return head; }
这段代码可以成功运行,对比前一段代码,可以看到如下不同:
- 后一段代码是list来接收返回值,前一段是将list当成参数传入
但为啥运行结果天差地别呢?
分析:第二种情况用list接收一个返回值,这是一定能成功的,。在函数中malloc了一个Node空间,然后将其地址返回,如此list的值就等于这个Node的地址了;
第一种情况是将list作为参数传入,我们知道,一旦涉及参数,就会有形参和实参,也就是说会有一个list的副本。这个副本和实参在数值上相等,但地址不同,所以如果在函数里修改这个副本的值是没有办法同步到实参的,只能通过地址来操作值才能使形参和实参同步。因此,传了list之后,会有一个形参list,它们的值(现在是NULL)相同,但地址不同。
而我们是想改变主函数中list的值,那么在函数中直接修改值肯定是不行的,那只是修改了副本的值,等函数执行完毕后,副本(在这个程序里是head)和pNew都会被回收。所以这么做不仅达不到修改主函数中list值的目的,反而还会造成内存泄漏( 因为,副本和pNew都被回收,没有其他指针指向申请的内存空间了 )。
-
其实想一想是很简单的,想要在函数里改变变量的值我们就得传地址;想要改地址的值,我们就得传地址的地址。在这个例子里面,我们不过是想改变主函数中指针的值而已,自然得传指针的地址。正确的代码如下:
main() Node *list=NULL; CreatNode(&list); printf("%d",list->data); Node * CreatNode(Node **head) { Node * pNew=(Node *)malloc(sizeof(Node)); pNew->data=0; pNew->next=NULL; *head=pNew; }
-
2020年1月5日于实验室,这几天想复习一下指针,正好看到链表这里,于是想起了此前一直被困扰的问题。其实不难,只不过是被指针给绕晕了,本质上还是传值和传址。
-
关于二重指针可以接着看这篇加强理解:https://blog.csdn.net/qq_39579087/article/details/104812065