###引言###
在数据结构的学习过程中,有时候会遇到一些一时无法理解的问题,深究起来却是语言的底层的语法机制所限制.
就例如在链表的构建中,链表的初始化和销毁为何需要使用一个二级指针,而不是只需要传递一个指针就可以了,其问题的关键就在于c语言的参数传递的方式是值传递
那么,这篇文章就来聊一聊在链表的初始化中一级指针的传递和二级指针的区别.
一级指针和二级指针的区别
1.前提知识:c语言中参数传递的方式是值传递和地址传递
- 值传递:传递的是实参的副本,即形参是一个新开辟的类型一样,里面的内容一样,地址不一样的一个变量,主函数和被被调用函数用的是不一样的内存空间
- 地址传递:传递的是一个地址,实际上主函数和被调用函数共用的是同一块内存空间,被调用函数依然需要一个新的指针来保存起来这块地址
2.传递一级指针:无法对原指针指向第地址进行修改
一级指针实例:
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 100
typedef int ElemType;
typedef struct SingleNode{
ElemType data;
struct SingleNode *next;
}SingleNodeList,*Linklist;
void LinkedListInit(SingleNodeList *head){//用一个指针head接收传入的地址
Linklist p;
if((head=(SingleNodeList *)malloc(sizeof(SingleNodeList)))==NULL){
exit(1);//给head分配内存,改变了head指针指向的地址(注意这里的head只是LinkedListInit的head,不是主函数那个)
}
head->next=NULL;
}//这个函数结束后head被销毁了,主函数的那个head不变;
int LinkedList_PushFront(SingleNodeList *head,ElemType x){//2单链表头插入
SingleNodeList *q;
if((q=(struct SingleNode *)malloc(sizeof (struct SingleNode)))==NULL){
exit(1);
}
q->data=x; q->next=head->next;//头节点的数据域与指针域赋值
head->next=q;//头节点加入链表
return 1;
}
int LinkedList_PopFront(SingleNodeList *head,ElemType *x){//3单链表头删除
SingleNodeList *p=head,*q;
if(p->next==NULL){
printf("There is no data in the Linkedlist to delete.\n");
*x = -12345;//未成功删除则在x指向单元赋特定值
return 0;
}
p=head->next;
q=p;
head->next=p->next;
*x=q->data;
free(q);
return *x;//请填写多行代码
}
int LinkedListGet_current(SingleNodeList *p,ElemType *x){//4取当前指针指数据
*x =p->data;
return 1;
}
int LinkedListUpdata_current(SingleNodeList *p,ElemType x){//5修改当前指针数据
p->data=x;
return 1;
}
int LinkedListShow(SingleNodeList *head){//6打印单链表
SingleNodeList *p=head;
if(p->next==NULL){
printf("There is no data in the Linkedlist to print.\n");
return 0;
}
while(p->next!=NULL){
printf("%d ",p->next->data);
p=p->next;
}
printf("\n");
return 1;
}
void LinkedListDestroy(SingleNodeList **head){//7释放链表
SingleNodeList *p=*head,*q;
while(p!=NULL){
q=p;
p=p->next;
free(q);
}
*head=NULL;
}
int LinkedListLength(SingleNodeList *head){//8求单链表长度
SingleNodeList *p=head;
int size=0;
while(p->next!=NULL){
size++;
p=p->next;
}
return size;
}
int main(){
SingleNodeList *head,*p;
ElemType i,x;
int switch_num;
scanf("%d",&switch_num);
switch(switch_num){
case 1:
LinkedListInit(head); //传入指针变量head的地址
LinkedList_PushFront(head,1);
LinkedList_PushFront(head,3);
LinkedList_PushFront(head,2);
LinkedListShow(head);
break;
}
LinkedListDestroy(&head);
return 0;
}
传递流程如图所示
从图中可以看出,main函数中我们定义了 一个指针head,假设它的地址是0x10010,但是还没给它初始化,也就是说它存的地址是随机的,我们也假设它存的是0x12306
在main函数中,我们把head这个指针作为参数传递进去初始化函数(值传递),按照值传递的原则,初始化函数首先开辟了一个*head指针,它的地址是0x12345(与main函数的0x10010不一样),但是它的内容是是和主函数的head是一样的,都是指向0x12306这个地址
在初始化的过程中,我们用malloc函数对初始化函数内的head指针分配内存空间,也就是改变了head指针的值,由未初始化的随机值0x12306改变成了0x10086
也就是说,由于这个head是作用在初始化函数内的,mallo作用的不是主函数的head,初始化函数结束后,这个head指针就被销毁掉了,主函数中的head不受影响,初始化失败,而分配了内存不能使用,造成了内存泄漏