链表中的分段错误

3

编辑:Dijkstra的回答是解决此问题的方法。我的列表没有初始化为NULL。

我正在编写一个链表,用于存储唯一的单词列表,当我尝试遍历该列表时会出现段错误。 Gdb 给了我以下信息:

    Program received signal SIGSEGV, 
    Segmentation fault. 0x0000003a07e47ff7 in vfprintf () from /lib64/libc.so.6 
    Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.7.el6_0.5.x86_64

列表的插入代码如下:
typedef struct L { char x[40] ; int occ; struct L *next ;} List;
List *insertList( char *in, List *l )
{
    List *t = calloc( 1, sizeof( List ) ) ;
    strcpy(t->x, in);
    t->occ = 1;
    t->next = l ;
    return t ;
}

void printList(List *l)
{
    List *l2 = l;
    while(l2)
    {
        printf("%s ", l2->x);
        l2 = l2->next;
    }
    return;
}

它正在循环单词,并将它们插入链表中,看起来没问题。当我遍历列表显示这些单词(大约4200个单词)时,大约98%的单词可以正常显示,然后它会在没有警告的情况下发生段错误。 经过更深入的检查,它以与添加顺序相反的顺序读回单词(这是有道理的),并且会在链表末尾的第五个单词(第五个添加的单词)之前崩溃。我已尝试调整插入函数以允许字符串超过40个字符,但插入到开头的单词(并造成段错误)都不到20个字符。 通过进一步挖掘,如果在printList函数中printf l2->next->next->next->next,则插入的第一个单词就在那里。 请问有谁能指导我正确的方向吗? 谢谢

请提供打印列表的代码。 - nmjohn
首先,养成使用 strncpy 而不是 strcpy 的习惯。 - thiton
一个单词的长度是否超过了39个字符?你应该使用strncpy来确保不会越界内存。 - ugoren
2
我认为在不确定边界的情况下,snprintf 应该成为习惯,而不是 strcpy。strncpy 实际上可能会使您的缓冲区溢出更加隐匿,因为它不能保证将字符串以 null 结尾。 - dbeer
calloc没有分配失败,是吗?另外,我会断言字符串in的长度在您的内部x缓冲区的范围之内。 - Michael Dorgan
4个回答

3

如果您不使用Linux,gdb(或其他调试器)是我追踪段错误的首选工具。使用带有调试符号的编译代码,并在调试器中运行它。当程序崩溃时,请检查导致崩溃的那一行代码。如有需要,使用回溯命令。遵循这些步骤,基本上总能找出如何修复段错误。


2
我的最佳猜测是,使用strncpy而不是strcpy可以解决你的问题。听起来像是某个地方在列表后期覆盖了你的“next”指针,并且如果“in”中的字符串太长,也会导致这种情况发生。
不要忘记,strncpy不会终止过长的字符串,因此请确保在末尾添加一个'\0'字符。
x[39]=0;

为确保字符串能够正确终止。


谢谢,我尝试了你建议的方法,但无济于事。稍微研究了一下,我发现由于某种原因插入的前几个单词导致了段错误。如果我删掉前几个单词,接下来的几个单词(在之前的例子中是没有问题的)现在就成了问题。 - ethangk
也许你的代码其他地方存在内存覆盖问题。 - Michael Dorgan

2
你是如何初始化第一个节点的?
你说“前几个单词导致了段错误”,但中断可能会停止它们的打印,实际问题可能出现在最后。
我的假设(只是猜测:P)是你的第一个节点没有“next = NULL;”,它只是未初始化的内存。因此,while循环无法检测到它已经到达列表的末尾,并尝试打印一些奇怪的东西,导致段错误。

我们有一个赢家。我一两个小时前设法让它工作了,但是这是问题所在。我不小心将另一个列表初始化为NULL。谢谢 :) - ethangk

2

这只是一种文体上的说法(可能不会受到SO网友的欣赏;-[ )。为什么要使用普通的for循环?当有一种有效的惯用结构可以处理这种情况时,为什么要在循环内迭代?

void printList(List *lp)
{
    List *l2;

    for(l2=lp; l2; l2 = l2->next)
    {
        printf("%s ", l2->x);
    }
    return;
}

老实说,我之前并没有考虑过尝试这种方法,但看起来更简洁。虽然最初的问题已经解决,但我真的很喜欢这种方法。谢谢。 - ethangk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接