清除链接列表中的最后一个元素

时间:2015-04-30 09:38:15

标签: c linked-list

我正在研究一个有链表的C程序。我需要从链表中删除最后一个元素,它主要工作,除非它遇到我的代码的特定部分,然后它有一个分段错误。

我的代码如下:

sed -n '/.*aaa\.bbb\.ccc\.\([0-9]\{1,\}\).*/ {s//\1/;H;}
   $!d
   s/.*//;H;x
:a
   s/\(\n[^[:cntrl:]]*\)\(.*\)\1\n/\1\2\
/
   ta
   s/.\(.*\)./\1/p' YourFile

它在int clearOutboundLegFromList(callLogSearchOutboundStruct ** outboundLeg, int dataCol, int rowTargets) { //callLogSearchOutboundStruct *currentStruct = *outboundLeg; //callLogSearchOutboundStruct *temp; if (*outboundLeg == NULL) { return 0; } SL_DebugAll(DBG_ALWAYS, "DEBUG: Clearing outbound legs: DataCol: %i RowTargets: %i", dataCol, rowTargets); callLogSearchOutboundStruct *legToRemove = NULL; callLogSearchOutboundStruct *last = NULL; legToRemove = *outboundLeg; while (legToRemove->nextLeg != NULL) { last = legToRemove; legToRemove = legToRemove->nextLeg; } if (legToRemove->target != NULL) { free(legToRemove->target); legToRemove->target = NULL; } if (legToRemove->cleardownCause) { free(legToRemove->cleardownCause); legToRemove->cleardownCause = NULL; } free(legToRemove); if (last != NULL) { last->nextLeg = NULL; } legToRemove = NULL; } 的行上崩溃了。

在核心转储中,我有以下内容:

free(legToRemove->target);

如果我从核心转储中打印legToRemove->目标gdb输出以下内容:

Program terminated with signal 11, Segmentation fault.
#0  0x00b01336 in _int_free () from /lib/libc.so.6
Missing separate debuginfos, use: debuginfo-install cyrus-sasl-lib-2.1.23-13.el6_3.1.i686 glibc-2.12-1.132.el6_5.2.i686 keyutils-libs-1.4-4.el6.i686 krb5-libs-1.10.3-15.el6_5.1.i686 libcom_err-1.41.12-18.el6.i686 libcurl-7.19.7-37.el6_5.3.i686 libidn-1.18-2.el6.i686 libselinux-2.0.94-5.3.el6_4.1.i686 libssh2-1.4.2-1.el6.i686 mysql-libs-5.1.73-3.el6_5.i686 nspr-4.9.2-1.el6.i686 nss-3.14.0.0-12.el6.i686 nss-softokn-freebl-3.12.9-11.el6.i686 nss-util-3.14.0.0-2.el6.i686 openldap-2.4.23-31.el6.i686 openssl-1.0.1e-16.el6_5.14.i686 zlib-1.2.3-29.el6.i686
(gdb) bt
#0  0x00b01336 in _int_free () from /lib/libc.so.6
#1  0x0805cd0b in clearOutboundLegFromList (outboundLeg=0xb5de7984, dataCol=9, rowTargets=11) at performreport.c:6731
#2  0x08058f33 in processDrilldownData (reportParameterArray=..., csvFile=0x8e3fc78, HandleDB=0xbfca7a14, resultReport=0x8e457a8, 

现在它看起来像是一个正确分配的内存空间,它只包含一个空字符串,所以我不明白为什么这会导致段错误。

1 个答案:

答案 0 :(得分:2)

您没有显示结构的外观或者如何将链添加到链接列表中,但是如果删除最后一个节点,则删除函数中会出现错误:在这种情况下,列表头应该是设为NULL

这种特殊情况是将列表头作为指向段指针的指针传递的原因:该函数必须能够在删除第一个节点时更新头部。如果你不这样做,调用函数中头部的值将是相同的,它将引用你只有free d的内存。访问这样的内存是违法的。

因此,代码的更新版本可能如下所示:

void clearOutboundLegFromList(callLogSearchOutboundStruct **outboundLeg)
{
    callLogSearchOutboundStruct *last = NULL;
    legToRemove = *outboundLeg;

    if (legToRemove == NULL) return;

    while (legToRemove->nextLeg) {
        last = legToRemove;
        legToRemove = legToRemove->nextLeg;
    }

    free(legToRemove->target);
    free(legToRemove->cleardownCause);
    free(legToRemove);

    if (last) {
        last->nextLeg = NULL;
    } else {
        *outboundLeg = NULL;        
    }
}

最后需要显式赋值,因为一旦初始化legToRemove,就只能使用该局部指针进行操作。

如果你对通过指向指针的双重间接感到更有信心,你可以迭代到最终没有局部变量:

void clearOutboundLegFromList(callLogSearchOutboundStruct **outboundLeg)
{
    if (*outboundLeg == NULL) return;

    while (*outboundLeg) {
        outboundLeg = &(*outboundLeg)->nextLeg;
    }

    free((*outboundLeg)->target);
    free((*outboundLeg)->cleardownCause);
    free(*outboundLeg);

    *outboundLeg = NULL;        
}

这将在删除第一个元素时自动更新头指针。这里的想法是outboundLeg在开始时指向头节点,在后续迭代时指向前一个节点的nextLeg指针。通过(*outboundLeg)的附加间接与通过nextLeg成员访问节点大致相同,除了第一个节点,通过头节点指针访问指针。

(分心:你的代码在释放成员指针时过于谨慎。free空指针是合法的;这不会做任何事情,但意味着你不必检查{{在客户端代码中。这样的检查可能仍然是很好的做法,因为许多函数不会采用空指针。如果这些指针仍然存在一段时间,将成员指针设置为NULL是一个好主意。但是你很快就会NULL包含结构。设置指向free的指针有点像在你拆掉房子之前清理浴室。将NULL设置为{{1在函数的末尾没有做任何事情:无论如何,指针都会超出范围。这只是我的短代码的一小部分和备注。你的检查没有错,最好小心谨慎。)< / p>