一、引入——指针与子函数的关系:
什么是指针?
指针是一种特殊的变量,类似于一般的变量存放着数据,指针存放着地址,通过这个地址可以指向空间,从而操作该空间内的数据
普通变量、指针变量在子函数中不同表现:
#include "stdio.h"
void swi_normal(int a,int b);
void swi_point(int *a,int *b);
void main()
{
int a=1,b=2;
//分别用普通变量和指针变量进行操作,并打印出结果
swi_normal(a,b);
printf("normal:a %d b %d\n",a,b);
swi_point(&a,&b);
printf("point:a %d b %d\n",a,b);
}
//普通变量操作
void swi_normal(int a,int b)
{
int temp;
temp = a;
a = b;
b = temp;
}
//指针变量
void swi_point(int *a,int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
(1)结果:
通过第一个操作,a,b交换失败;而第二个操作,a,b交换成功
(2)分析:
因为第一个函数是传入数据,然后在子函数中在进行申请空间来存放数据,子函数结束后会自动释放这些空间;第一个函数是传入地址,子函数结束后不会释放这些空间
二、推广到二重指针:
以建立顺序表为例:
#include "stdio.h"
#include <stdlib.h>
#define datatype int
#define MAXSIZE 4
typedef struct
{
datatype data[MAXSIZE]; //存储顺序表中的元素
int len; //顺序表的表长
}SeqList; //顺序表类型
void CreatList_Normal(SeqList *L);
void CreatList_Point(SeqList **L);
SeqList *Init_SeqList();
void main()
{
SeqList *L1,*L2;
//初始化
L1 = Init_SeqList();
L2 = Init_SeqList();
//输入数据
CreatList_Normal(L1);
CreatList_Point(&L2);
//打印输入后的情况
printf("Normal:%d %d %d\n",L1->data[1],L1->data[2],L1->data[3]);
printf("Point: %d %d %d\n",L2->data[1],L2->data[2],L2->data[3]);
}
SeqList *Init_SeqList()
{
SeqList *L;
L=(SeqList*)malloc(sizeof(SeqList));
L->len=0;
return L;
}
void CreatList_Normal(SeqList *L)
{
int i, n;
printf("Input length of List: ");
scanf("%d", &n);
printf("Input elements of List: \n");
for(i=1;i<=n;i++)
scanf("%d", &L->data[i]);
L->len=n;
}
void CreatList_Point(SeqList **L)
{
int i, n;
printf("Input length of List: ");
scanf("%d", &n);
printf("Input elements of List: \n");
for(i=1;i<=n;i++)
scanf("%d", &(*L)->data[i]);
(*L)->len=n;
}
(1)结果:
(2)分析:
两种方式没有区别,因为顺序表的结构体中只含有数据,只需要指向这些数据的地址就可以改变了,所以只需要一重指针。
以建立链表为例:
#include <stdio.h>
#include <stdlib.h>
#define datatype char
typedef struct node
{
datatype data; //data为结点的数据信息
struct node *next; //next为指向后继结点的指针
}LNode; //单链表结点类型
void CreateLinkList_Normal(LNode *head);
void CreateLinkList_Point(LNode **head);
void main()
{
LNode *L1,*L2;
//对L1初始化,使L1存有某空间地址
L1 = (LNode *)malloc(sizeof(LNode));
//分别建立链表
CreateLinkList_Normal(L1);
CreateLinkList_Point(&L2);
}
void CreateLinkList_Normal(LNode *head)
{ //将主调函数中指向待生成单链表的指针地址(如&p)传给*head
char x;
LNode *p;
head=(LNode *)malloc(sizeof(LNode)); //在主调函数空间生成链表头结点
head->next=NULL; //*head为链表头指针
printf("L1 Input: \n");
scanf("%c", &x); //结点的数据域为char型,读入结点数据
while(x!='\n') //生成链表的其他结点
{
p=(LNode *)malloc(sizeof(LNode)); //申请一个结点空间
p->data=x;
p->next=head->next; //将头结点的next值赋给新结点*p的next
head->next=p; //头结点的next指针指向新结点*p实现在表头插入
scanf("%c", &x); //继续生成下一个新结点
}
}
void CreateLinkList_Point(LNode **head)
{ //将主调函数中指向待生成单链表的指针地址(如&p)传给**head
char x;
LNode *p;
*head=(LNode *)malloc(sizeof(LNode)); //在主调函数空间生成链表头结点
(*head)->next=NULL; //*head为链表头指针
printf("L2 Input: \n");
scanf("%c", &x); //结点的数据域为char型,读入结点数据
while(x!='\n') //生成链表的其他结点
{
p=(LNode *)malloc(sizeof(LNode)); //申请一个结点空间
p->data=x;
p->next=(*head)->next; //将头结点的next值赋给新结点*p的next
(*head)->next=p; //头结点的next指针指向新结点*p实现在表头插入
scanf("%c", &x); //继续生成下一个新结点
}
}
(1)结果:
(2)分析:
我们可以看到L1的数据没有发生变化,还是刚初始化好的值,L2的数据发生了改变,为什么会这样呢?
单链表结点类型中包含了一个指针变量,在顺序表中我们只需要指向整型变量就行,而在链表中我们需要指向指针变量next,类似于第一个例子,我们要改变指针变量的值就要传指针变量的地址进去,我们就要用到指针的指针(地址的地址)——二重指针