[英]Clear the last element from a linked list
我正在使用具有链接列表的C程序。 我需要从链接列表中删除最后一个元素,并且该元素通常可以正常工作,除非当它到达我的代码的特定部分时,它就会出现分段错误。
我拥有的代码如下:
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);
的行上崩溃free(legToRemove->target);
。
在核心转储中,我具有以下内容:
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,
如果我从核心转储中打印legToRemove-> target gdb,则会输出以下内容:
$1 = 0x99235d8 ""
现在,它看起来像是已正确分配的内存空间,它只包含一个空字符串,所以我不明白为什么这会导致段错误。
您没有显示结构的外观或向链接列表中添加分支的方式,但是如果删除最后一个节点,则会在删除函数中出现错误:在这种情况下,列表头应设置为NULL
。
这种特殊情况是将列表头作为指向leg指针的指针的原因:当删除第一个节点时,该函数必须能够更新头。 如果你不这样做,在调用函数头部的值将是相同的,它会引用您刚才内存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
有点像在拆掉房子之前打扫浴室。在函数的末尾将legToRemove
设置为NULL
不会做任何事情:指针将超出范围,这只是我的一个保留意见较短的代码。您的检查没有错,请谨慎行事。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.