现在的位置: 首页 > 综合 > 正文

c语言中一个指向指针的指针所引起的错误

2013年10月19日 ⁄ 综合 ⁄ 共 2827字 ⁄ 字号 评论关闭

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
int a;
struct lnode *next;
}node;   //单链表的节点的声明

void show(node *L)   //显示链表中的所有节点的值
{
node *temp;
temp = L->next;
while (temp != NULL)
{

printf("%d\n", temp->a);
temp = temp->next;
}
}
int CreateList(node *L, int n)  //常见n个节点,从终端取得输入////////////////////////////////////////////
{
L = (node *)malloc(sizeof (node));  //创建头节点
L->next = NULL;

node *R;   //创建尾节点
R = L;

node *temp;  //零时节点指针

int i; //循环计数变量

for (i = 1; i <= n; i++)
{
temp = (node *)malloc(sizeof (node));
if (temp == NULL)
return 0;
R->next = temp;
R = temp;
R->next = NULL;
R->a = i;
}
return 1;
}
int main()
{
node *T = NULL;     //头节点
CreateList(T, 3);  //创建3个节点的单链表,从终端接收输入////////////////////////////////////////
show(L);   //显示链表中的所有节点
return 0;
}

  用gdb调试时出现错误:
Program received signal SIGSEGV, Segmentation fault

   这种错误很常见,但在网上确没有一个人讲的很清楚。现在结合上面的程序进行一些分析,由于只是简单的测试程序,所以上面的程序写的不太规范,也有些简单。但对于说明问题已经 完全足够了。好了废话不多说了,现在来看看这里出现的错误。
   上面的程序在gcc中编译时不会报错误的(本人已经试过了,无须怀疑)。但运行时总是提示段错误。这完全是一个c中的语法错误引起的。
   请你注意有特殊提示的两处地方,这是一个链表程序,在主函数中只定义了一个头指针在调用CreateList函数(根据输入创建链表函数)时主函数向其传递T的值(注意是T的值),而在子函数中用malloc分配了新的节点的内存单元,并将其返回的节点的指针赋值给L但这没有改变主函数中T的值(将此时的头节点看成一个变量,即该变量的值没有改变),则T没有指向新分配的内存(头节点)。当子函数返回时,自动变量(此处关键是变量L)将被释放掉,则创建的链表的头节点地址就丢失掉了。后面再用ShowList来显示的链表时,就不知道访问的是那个内存单元中的数据了,故gdb调试时报段错误。此处的关键是没有在子函数中创建链表时,改变头指针T的值,没有使其指向已经创建的链表。

   上面的程序在gcc中编译时不会报错误的(本人已经试过了,无须怀疑)。但运行时总是提示段错误。这完全是一个c中的语法错误引起的。
   请你注意有特殊提示的两处地方,这是一个链表程序,在主函数中只定义了一个头指针在调用CreateList函数(根据输入创建链表函数)时主函数向其传递T的值(注意是T的值),而在子函数中用malloc分配了新的节点的内存单元,并将其返回的节点的指针赋值给L但这没有改变主函数中T的值(将此时的头节点看成一个变量,即该变量的值没有改变),则T没有指向新分配的内存(头节点)。当子函数返回时,自动变量(此处关键是变量L)将被释放掉,则创建的链表的头节点地址就丢失掉了。后面再用ShowList来显示的链表时,就不知道访问的是那个内存单元中的数据了,故gdb调试时报段错误。此处的关键是没有在子函数中创建链表时,改变头指针T的值,没有使其指向已经创建的链表。

   知道了问题的根源就好办了,经过和同学的讨论,我们得到了三种方法:

方法一:(这是我同学的想法)
在主函数中创建两个指针,一个是头指针(同上面一样),一个是指向头指针的指针,在子函数中用指向节点指针的指针作为参数,这样就可以在子函数中改变头指针的值。(具体的源代码就不给了)

这种方法有个明显的缺点就是要在主函数中维护两个指针,这不是一个好的编程习惯。

方法二:
可以将头节点的定义在主函数中实现,这样传递给子函数的就是头节点的地址了,只需要通过这个地址改变头节点的指向后继节点的指针就可以完成链表的创建工作。

这种方法将头节点的定义在主函数中实现,有时是不能满足要求的。

方法三:
主函数中源代码的修改:
CreateList(&T, 3); //即传递这个头指针的地址给子函数
子函数声明的修改:

int CreateList(node *(*L), int n);//即参数声明为指向节点指针的指针,这样就可以在子函数中通过(*L)来         代替T的作用,并可以改变T的值。

完整的修改程序如下(如果有什么问题请留言,或发邮件到yangxiaodong789@yeah.net)
关于使用gdb调试程序此处推荐一篇文章:GDB应用实例


//下面的程序已经在gcc中调试通过,运行结果为:

1
2
3


通过第三种方法修改的完整源代码为:

#include<stdio.h>
#include<stdlib.h>
typedef struct lnode
{
int a;
struct lnode *next;
}node;   //单链表的节点的声明

void show(node *L)   //显示链表中的所有节点的值
{
node *temp;
temp = L->next;
while (temp != NULL)
{

printf("%d\n", temp->a);
temp = temp->next;
}
}
int CreateList(node *(*L), int n)  //常见n个节点,从终端取得输入////////////////////////////////////////////
{
(*L) = (node *)malloc(sizeof (node));  //创建头节点
(*L)->next = NULL;

node *R;   //创建尾节点
R = (*L);

node *temp;  //零时节点指针

int i; //循环计数变量

for (i = 1; i <= n; i++)
{
temp = (node *)malloc(sizeof (node));
if (temp == NULL)
return 0;
R->next = temp;
R = temp;
R->next = NULL;
R->a = i;
}
return 1;
}
int main()
{
node *T = NULL;     //头节点
CreateList(&T, 3);  //创建3个节点的单链表,从终端接收输入////////////////////////////////////////
show(T);   //显示链表中的所有节点
return 0;
}

抱歉!评论已关闭.