关于指针在函数方面应用的疑惑!

fenxue 2007-07-04 12:33:48
今天,我在某本书上看到链表的完整操作过程,小弟可能刚入门,有个非常大的疑惑,我先贴源代码:
/*单链表的各种操作*/

# define null 0

typedef char ElemType; /* 字符型数据*/

typedef struct LNode
{
ElemType data;
struct LNode *next;
};

setnull(struct LNode **p);
int length (struct LNode **p);
ElemType get(struct LNode **p,int i);
void insert(struct LNode **p,ElemType x,int i);
int delete(struct LNode **p,int i);
void display(struct LNode **p);

main()
{
struct LNode *head,*q; /*定义静态变量*/
int select,x1,x2,x3,x4;
int i,n;
int m,g;
char e,y;

head=setnull(&head); /*建议链表并设置为空表*/
printf("请输入数据长度: ");
scanf("%d",&n);
for(i=1;i<n;i++);
{
printf("将数据插入到单链表中: ");
scanf("%d",&y);
insert(&head,y,i);} /*插入数据到链表*/
display(&head); /*显示链表所有数据*/

printf("select 1 求长度 length()\n");
printf("select 2 取结点 get()\n");
printf("select 3 求值查找 locate()\n");
printf("select 4 删除结点 delete()\n");
printf("input your select: ");
scanf("%d",&select);
switch(select)
{
case 1:
{
x1=length(&head);
printf("输出单链表的长度%d ",x1);
display(&head);
}break;

case 2:
{
printf("请输入要取得结点: ");
scanf("%d",&m);
x2=get(&head,m);
printf(x2);
display(&head);
}break;

case 3:
{
printf("请输入要查找的数据: ");
scanf("%d",&e);
x3=locate(&head,e);
printf(x3);
display(&head);
}break;

case 4:
{
printf("请输入要删除的结点: ");
scanf("%d",&g);
x4=delete(&head,g);
printf(x4);
display(&head);
}break;
}
}
}


setnull(struct LNode **p)
{
*p=null;
}

int length (struct LNode **p)
{
int n=0;
struct LNode *q=*p;
while (q!=null)
{
n++;
q=q->next;
}
return(n);
}

ElemType get(struct LNode **p,int i)
{
int j=1;
struct LNode *q=*p;
while (j<i&&q!=null)
{
q=q->next;
j++;
}
if(q!=null)
return(q->data);
else
printf("位置参数不正确!\n");
}

int locate(struct LNode **p,ElemType x)
{
int n=0;
struct LNode *q=*p;
while (q!=null&&q->data!=x)
{
q=q->next;
n++;
}
if(q==null)
return(-1);
else
return(n+1);
}

void insert(struct LNode **p,ElemType x,int i)
{
int j=1;
struct LNode *s,*q;
s=(struct LNode *)malloc(sizeof(struct LNode));
s->data=x;
q=*p;
if(i==1)
{
s->next=q;
p=s;
}
else
{
while(j<i-1&&q->next!=null)
{
q=q->next;
j++;
}
if(j==i-1)
{
s->next=q->next;
q->next=s;
}
else
printf("位置参数不正确!\n");
}
}

int delete(struct LNode **p,int i)
{
int j=1;
struct LNode *q=*p,*t;
if(i==1)
{
t=q;
*p=q->next;
}
else
{
while(j<i-1&&q->next!=null)
{
q=q->next;
j++;
}
if(q->next!=null&&j==i-1)
{
t=q->next;
q->next=t->next;
}
else
printf("位置参数不正确!\n");
}
if(t=null)
free(t);
}

void display(struct LNode **p)
{
struct LNode *q;
q=*p;
printf("单链表显示: ");
if(q==null)
printf("链表为空!");
else if (q->next==null)
printf("%c\n",q->data);
else
{
while(q->next!=null)
{
printf("%c->",q->data);
q=q->next;
}
printf("%c",q->data);
}
printf("\n");
}

在这段代码中,每个函数中的参数都有struct LNode **p,比哪void display(struct LNode **p)这个函数,如果我将它改为void display(struct LNode *p),然后用q=p;进行赋值,结果还不是一样的吧,为什么要传指针的指针,我一直想不通-郁闷中,有没有谁帮我解答这个问题?
...全文
463 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
lishi108 2010-12-09
  • 打赏
  • 举报
回复
学习了
fenxue 2007-07-19
  • 打赏
  • 举报
回复
致:zhangyanli()
我个人认为增加一个头结点犹如uwinb所说的那样,比如存储节点数点等,当然结点数据类型要符合才行。这样当要求链表的结点数时,不就很方便吗?其实还有一点我认为,当声明一个指针指向结点时,确实声明了指针头结点,但只能存储头结点的地址值而已。
zhangyanli 2007-07-14
  • 打赏
  • 举报
回复
re:yl02520
typedef struct LNode
{
ElemType data;
struct LNode *next;
}*LinkList;这样写确实不错,在其他的函数中传参也方便多了,但是指针的引用用的是在不多见,最好还是指针的指针。

另外除了uwinb所说的关于头节点的好处外,再补充一些,其实头节点本身就是(如果专门定义)为了增删操作方便而引入的一个辅助节点,他不应当存储任何真实节点信息,当然可以如uwinbs所说的等有用信息。这样的话,在增删时,就可以一是同仁的对待所有节点,而不必花费过多精力在第一个节点的插入和删除上。
尹立 2007-07-13
  • 打赏
  • 举报
回复
上面那个写错了一点
应该是下面这个
typedef struct LNode
{
ElemType data;
struct LNode *next;
}*LinkList; //很多时候都是这样写的
尹立 2007-07-13
  • 打赏
  • 举报
回复
致:fenxue (Newton.cld)
对可以这么说,不过在链表中一般传进来的都是指针,所以LinkList &L几乎没看见过,不过有的在定义时直接把LinkList定义成指针的形式,例如
typedef struct LNode
{
ElemType data;
struct LNode *next;
}LinkList*; //很多时候都是这样写的
再看LinkList &L就很舒服了
fenxue 2007-07-12
  • 打赏
  • 举报
回复
uwinb()先生:
给这个链表增加一个结点:Head.next=new LNode; (Head.next)->next=NULL;
后面所说应该就是拿这个结点去存储链表长度等方面的信息吧?,现在传指针的指针问题解决了,先谢谢了。
yl02520(立立)先生回复有所感悟,使用LinkList *&L,就是为了保证传进来的参数是个指针,如果有其它函数,比如封装插入结点、删除之类的操作,可以将它改为LinkList &L,即传引用,则就可以对链表实行操作,不知道我说的对不?
尹立 2007-07-12
  • 打赏
  • 举报
回复
致:fenxue (Newton.cld)

程序代码我没看,说说我的看法吧
如果调用在链表中进行删除和插入等改变链表的函数时,应该把要链表的头指针的地址作为参数
相反如果不用&head,只用head的话,例如:在head后面插入一个节点,那么在程序返回后,实际
上,在原函数中,head的next根本没有变化。你可以自己简单调试一下,记得以前自己也出现过
类似问题

还有为什么要使用LinkList *&L呢?以为首先你要保证传进来的参数是个指针呀,如果不是指针的话,后面的L=(LinkList *)malloc(***);就会出错
uwinb 2007-07-12
  • 打赏
  • 举报
回复
牛顿先生:
如果你真的很想定义 void CreateList(LNode &L, elemtype a[], int n)
我得提醒你,要重新认识一下“空链表”这个概念,在以往它是指 LNode* phead=NULL;
现在我们定义 LNode Head={'0',NULL}; 就是一个空链表,仅管它的确有一个结点,但这个结点是不被使用的,给这个链表增加一个结点:Head.next=new LNode; (Head.next)->next=NULL;

这样定义链表有一个好处,就是在增删结点时,头结点是永恒不变的,并且头结点中的数据域还可以用来存放链表长度这一不能在常数时间内获取的信息(当然你程序中data是char型,用于记录链表长度表示范围太小了)。

如果接受上面的概念,你的程序上上下下要改很多地方,比如输出链表时不要显示头结点的值。
kimiya25 2007-07-11
  • 打赏
  • 举报
回复
c中一般就是用指针的指针了....例子就如上面所说的分配内存空间一样..
fenxue 2007-07-11
  • 打赏
  • 举报
回复
再致uwinb():
将函数改为void CreateList(LinkList &L,elemtype a[],int n)//将指针引用改为引用,好像也是可以的。
但后面的函数就遇到了问题,如顺序存储的线性表初始化问题如:
void InitList(SqList *&L)
{
L=(SqList *)malloc(sizeof(SqList));//分配内存
L->length=0;//线性表的长度
}
如果将上述改void InitList(SqList &L)好像不通施行上述的内存分配,或者将它改为如下
SqList *InitList(SqList *L)或void InitList(SqList **L),还有就是上面的方法了,不知道我说的对不对,敬请论坛的大哥大姐多多点评!
uwinb 2007-07-11
  • 打赏
  • 举报
回复
void CreateList(LinkList *&L,elemtype a[],int n) //引用指针是由程序上下文所决定的!
一般建链表时首先想到定义 struct LNode *head; 有没有想过不把head定义成指针呢?

其实对于创建链表这个函数最好定义成:LinkList* CreateList(elemtype a[], int n)

不修改链表结构的话当然就简单些了,比如:void display(struct LNode *p)
仅仅修改链表结点数据域的值,不是在头结点处进行向前插入,都没有使用二级指针的必要
fenxue 2007-07-11
  • 打赏
  • 举报
回复
致uwinb():
使用二级指针主要是为了解决在函数内部改变链表头结点指针head的问题(要返回对它的修改),如果一个函数对链表结构没有修改,应该使用单指针,就可以了吧!
fenxue 2007-07-11
  • 打赏
  • 举报
回复
谢谢了,小弟还有一事不明白?
在C++存在引用,我用下面的代码来描述单链表的基本运算:

typedef struts DNode
{
elemType data;
struts DNode *next;
}LinkList;

//建立单链表,采用头插法
void CreateList(LinkList *&L,elemtype a[],int n)
{
LinkList *S;
int i;
L=(LinkList *)malloc(sizeof(LinkList));//创建头结点
L->next=NULL;
for(i=0;i<n;i++)
{
S=(LinkList *)malloc(sizeof(LinkList));//创建新结点
S->data=a[i];
S->next=L->next;
L->next=S;
}
}

终于将代码敲完了,我想问的问题是void CreateList(LinkList *&L,elemtype a[],int n)中,
“&”代表引用,即是LinkList的别名了,为什么还要用引针,难道也是为了传回指针?
我上次买了“李春葆”的《数据结构教程(2版)》,我发了邮件给他,居然不回复我,气死我了,求论坛的大哥大姐相救啊!
zhangyanli 2007-07-09
  • 打赏
  • 举报
回复
张见识了谢谢了
uwinb 2007-07-09
  • 打赏
  • 举报
回复
修正一下:传值意味着修改形参不影响实参的值
uwinb 2007-07-08
  • 打赏
  • 举报
回复
使用二级指针主要是为了解决在函数内部改变链表头结点指针head的问题(要返回对它的修改)
还有其它解决方案,如将head定义成全局变量,或者规定空链表也有一个固定结点。

关于C函数的参数传递方式,你要记住只有传值一种!(传值意味着修改实参不影响形参的值)
说什么地址传递,只会越描越黑,(仅管它是官方说法,它们喜欢上升到理论层次上去解说)

fun(char *p){p="hi";} main(){char* str="welcome"; fun(str); puts(str);}
查看运行结果可以发现指针str的值没有被fun()函数改变。

fun(char **pp){*pp="hi"} main(){char* str="welcome"; fun(&str); puts(str);}
函数fun()不能改变实参(&str)的值,但它可以修改这个二级指针所指向的指针str的值。
zhangyanli 2007-07-07
  • 打赏
  • 举报
回复
不错有的,我觉得
不过我以前还真没注意过他(指针的指针参数)的好处,有收获谢谢
fenxue 2007-07-06
  • 打赏
  • 举报
回复
对这个函数setnull(struct LNode **p)
{
*p=null;
}
应该也影响吧,按照上面的提议
uzone 2007-07-04
  • 打赏
  • 举报
回复
除了insert之外,对其他的应该没什么影响.
uzone 2007-07-04
  • 打赏
  • 举报
回复
比如,int *s=NULL;
我要用函数对s分配空间。
fun1(&s);//ok
fun2(s);//false
s=fun3(3);//ok

void fun1(int **p)
{
*p=(int *)malloc(sizeof(int)*10);
}

void fun2(int *p)
{
p=(int *)malloc(sizeof(int)*10);//只是分配给了形参临时变量
}

int* fun3(int *p)
{
p=(int *)malloc(sizeof(int)*10);
return p;

}
加载更多回复(3)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧