某一天,我在学习链表函数的时候,学习浙大老师翁恺的课看到有个问题很疑惑,把它简化了下
趁着有点感觉,赶紧把它写成blog,免得日后忘了
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct node {
int value;
struct node* next;
}Node;
void add(Node* head, int number)
{
Node* p = (Node*)malloc(sizeof(Node));
p->value = number;
p->next = NULL;
Node* last = head;
if (last)
{
while (last->next)
{
last = last->next;
}
last->next = p;
}
else
{
head = p;//这里有问题!!!(1)
}
}
int main(void)
{
Node* head =NULL;
int number;
do {
scanf("%d", &number);
if (number != -1)
{
add(head, number);//这里有问题!!!
}
} while (number!=-1);
return 0;
}
代码看起来好像有点长,而且我没写注释,可能看起来比较痛苦,没关系!那就看下面这个我把问题移植到一个小代码段的下面这个吧(甚至于我把这个简化到链表都不存在了好像,只用了一个结构体)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct node {
int value;
struct node* next;
} Node;//这里定义一个结构体,里面存一个数据(整数)+一个指向下一个同类型结构体的指针为Node
void add(Node* head);//该函数在20多行代码里面的功能就是想让头结点链起那个创建的节点
int main()
{
Node* head = NULL;//创建头结点,并初始化为NULL;
add(head);
printf("%d\n", head->value);
return 0;
}
void add(Node* head)
{
Node* p = malloc(sizeof(Node));
p->value = 1;
p->next = NULL;
head = p;
}
结果看见了吗?啥也没有啊,按道理来讲p所指的那个结构体看起来已经完完全全赋给了head所指的那个结构体啊(虽然一开始head指向的是NULL)。但真的是这样吗?NO!
你再看看
看出来了?参数的形参换了,结果还是个寂寞!
原因:
1)我们都知道,函数传参的本质是值的传递!add(head);是啥子意思?head在这上面是个指向Node的指针,你把head作为参数传进去,相当于把head这个变量的值给传了进去,就算他是个指针变量,那也是变量!这个值是个NULL空咧!
2)函数传参之形参的性质:我不管你这个值是什么,现在我的形参是kkk,那我add函数就临时造一个变量来存这个NULL,当然,该临时变量是Node*类型的,然后个临时被造出来的变量,当然跟你那个add函数外面的那个head变量没有八杠子关系啊,他俩住在不同的地方啊!你把那个动态分配内存的那个p(指向一个结构体)赋给了kkk,跟另一个地方head他没有任何关系咧,head还是个孤家寡人啊,p你这人给东西给错了啊。。。
所以解决办法呢?
1):add函数返回那个临时变量kkk的地址,因为这个地址已经指向了一个p所指的结构体,你再把这个地址交给head呗,两人共用(应该也不是,毕竟add函数调用完毕之后,那个kkk就无了,没了,就只剩head了O(∩_∩)O哈哈~)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct node {
int value;
struct node* next;
} Node;//这里定义一个结构体,里面存一个数据(整数)+一个指向下一个同类型结构体的指针为Node
Node* add(Node* head);//该函数在20多行代码里面的功能就是想让头结点链起那个创建的节点
int main()
{
Node* head = NULL;//创建头结点,并初始化为NULL;
head= add(head);
printf("%d\n", head->value);
return 0;
}
Node* add(Node* kkk)
{
Node* p = malloc(sizeof(Node));
p->value = 1;
p->next = NULL;
kkk = p;
return kkk;
}
2)二级指针法(链表函数常用到的,优先记住这种方法)
最终的目的是让这个head指向那个当前已经创建好的结构体p,那就试图传个head这个指针的地址传进去,通过head所在的地址对head动点手:解引用他的地址拿起来做左值,即*(head的地址)=p,
那么形参应该怎么整呢?首先这形参肯定是一个指针用来存head的地址,那首先他的形式可以设置成...*kkk,表明kkk是个指针,至于指向啥类型?存head的地址,那就肯定指向的是head了,而head是个啥?它是个Node*类型的啊,所以我们就把它设置成Node** kkk,kkk是个指针,他指向的是是个Node*类型的数据,所以代码实现:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct node {
int value;
struct node* next;
} Node;//这里定义一个结构体,里面存一个数据(整数)+一个指向下一个同类型结构体的指针为Node
void add(Node** head);//该函数在20多行代码里面的功能就是想让头结点链起那个创建的节点
int main()
{
Node* head = NULL;//创建头结点,并初始化为NULL;
add(&head);
printf("%d\n", head->value);
return 0;
}
void add(Node** kkk)//你要觉得别扭,那就把kkk全部换成head,反正这函数里面的是个形参
{
Node* p = malloc(sizeof(Node));
p->value = 1;
p->next = NULL;
*kkk = p;
}