2015-04-30 2 views
1

Я работаю над программой на языке 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);.

В дамп я следующее:

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-> цель GDB выводит следующее:

$1 = 0x99235d8 "" 

Теперь, похоже, его правильно выделенное пространство памяти, оно просто содержит пустую строку, поэтому я не понимаю, почему это вызовет segfault.

Спасибо за любую помощь, которую вы можете предоставить.

+1

Вы не даете достаточно информации, чтобы найти настоящую причину. Но вот две возможные причины: поле 'target' должно быть выделено' malloc', если вы хотите «освободить» его. Либо вы этого не сделали (потому что 'target' - это массив символов в структуре, например' target [20] ', или потому, что это просто указатель на какую-либо другую память) или вы изменили значение' target' после его выделения. –

+0

Я не уверен, что если (legToRemove-> cleardownCause), почему бы не if (NULL! = LegToRemove-> cleardownCause) – flotto

+0

В чем смысл аргументов функции ', int dataCol, int rowTargets)'. Они, кажется, не используются. Предназначены ли они для определения элемента, который нужно удалить? – joop

ответ

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, вы работаете только с этим местным указателем.

Если вы чувствуете себя более уверенно с двойным indirections через указатели на указатели, можно перебирать до конца без местных variabes:

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 в конце функции ничего не делает: указатель в любом случае выйдет за рамки. Это всего лишь в сторону и retionale для моего более короткого кода. Ваши чеки не ошибаются, и лучше быть осторожными.)

+0

Спасибо за подробный ответ очень полезно. Я пошел на первый вариант выполнения * outboundLeg = NULL, и это, похоже, решило проблему. – Boardy

Смежные вопросы