簡體   English   中英

清除鏈接列表中的最后一個元素

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM