Я пытаюсь удалить слово (значение char*) из связанного списка, но получаю исключение, когда сравниваю свое слово со словом узла списка ссылок.
EXC_BAD_ACCESS (код=1, адрес=0xa00000002)
Я не уверен, почему это происходит, я был бы признателен за любое решение проблемы.
LinkedList* DeleteWordElement(LinkedList* head, char* value){
LinkedList *previous=head, *current=head->next;
if (head == NULL)
return head;
if (head->data == value)
{
LinkedList *temp = head;
head = head->next;
free(temp);
return head;
}
while (previous!=NULL)
{
if (previous->data == value) // Exception
break;
current = previous;
previous = previous->next;
}
if (previous != NULL)
current->next = previous->next;
free(previous);
return head;
}
Прежде чем делать:
LinkedList *previous=head, *current=head->next;
вам нужно проверить, является ли head
NULL
. Тогда вы должны использовать strcmp вместо ==
, чтобы сравнить C-строку value
:
strcmp сравнивает фактическое содержимое C-строки, используя == между две C-строки спрашивают, указывают ли эти два указателя на один и тот же символ позиция. (источник)
Наконец, вам не нужно держать указатель previous
, достаточно указателя current->next
:
LinkedList* DeleteWordElement(LinkedList* head, char* value){
if (head == NULL)
return head;
if (strcmp(value, head->data) == 0){
LinkedList *next = head->next;
free(head);
return next;
}
LinkedList *current=head;
while (current->next != NULL){
if (strcmp(value, current->next->data) == 0){
LinkedList *next = current->next;
current->next = current->next->next;
free(next);
break;
}
current = current->next;
}
return head;
}
Код можно сократить, если использовать рекурсивную версию, хотя и за счет производительности:
LinkedList* DeleteWordElement(LinkedList* current, char* value){
if (current != NULL){
if (strcmp(value, current->data) == 0){
LinkedList *result = current->next;
free(current);
return result;
}
current->next = DeleteWordElement(current->next, data);
}
return current;
}
Как уже было указано в другом ответе и в разделе комментариев, выражение
current=head->next
вызовет неопределенное поведение, когда head == NULL
.
Другая проблема заключается в том, что выражение
head->data == value
не сравнивает фактическое содержимое строки, а вместо этого сравнивает сами указатели (т.е. адреса памяти). Это не то, что вам нужно, потому что указатели, вероятно, всегда будут разными, даже если содержимое строки идентично. Чтобы сравнить фактическое содержимое строки, вы должны вместо этого использовать функцию strcmp.
Другой ответ уже содержит решение проблемы. Однако я хотел бы предоставить альтернативное решение, которое короче и эффективнее для компьютера, но, возможно, его сложнее понять программисту, поскольку оно использует двойные указатели (т.е. указатели на указатели):
LinkedList* DeleteWordElement( LinkedList *head, char* value )
{
LinkedList **pp = &head, *p;
while ( (p=*pp) != NULL )
{
if ( strcmp( p->data, value ) == 0 )
{
//found node, so unlink and remove it
*pp = p->next;
free( p );
break;
}
pp = &p->next;
}
return head;
}
Как видите, для этого решения требуется только один оператор if
, в отличие от кода в вашем ответе, который требует 4 оператора if
, и другого ответа, для которого требуется 3 оператора if
. Эти дополнительные операторы if
не нужны при использовании двойных указателей, потому что один и тот же код может обрабатывать все случаи. Поэтому вам не нужно вводить дополнительные пути кода для каждого отдельного случая. Как следствие, это решение также не требует дублирования кода.
Также стоит упомянуть, что сигнатура функции
LinkedList* DeleteWordElement(LinkedList* head, char* value)
немного неэффективен. Возвращаемое значение — это новый заголовок, поэтому код, вызывающий функцию, должен обновить заголовок списка на основе возвращаемого значения. Было бы проще, если бы код, вызывающий функцию, просто передавал бы адрес указателя на начало списка, чтобы функция могла сама обновить заголовок списка, когда это необходимо. Таким образом, коду, вызывающему функцию, не придется выполнять дополнительную работу.
Для этого вы можете изменить сигнатуру функции на следующую:
void DeleteWordElement( LinkedList** pp_head, char* value )
Поскольку адрес указателя является указателем на указатель (то есть двойным указателем), теперь вы должны использовать **
вместо только *
.
Кроме того, теперь, когда вы больше не используете возвращаемое функцией значение, вы можете захотеть использовать его для чего-то другого. Например, вы можете захотеть, чтобы функция возвращала значение независимо от того, найдено значение или нет. Поэтому вы можете изменить подпись на следующую:
bool DeleteWordElement( LinkedList** pp_head, char* value )
Теперь можно сделать функцию return true
, когда значение было найдено, иначе return false
указать, что значение не найдено. Обратите внимание, что вы должны #include <stdbool.h>
иметь доступ к bool
, true
и false
.
Хотя мы сделали функцию более мощной, добавив логическое возвращаемое значение, она так же проста, как и раньше.
bool DeleteWordElement( LinkedList **pp_head, char* value )
{
LinkedList **pp = pp_head, *p;
while ( (p=*pp) != NULL )
{
if ( strcmp( p->data, value ) == 0 )
{
//found node, so unlink and remove it, and then return true
*pp = p->next;
free( p );
return true;
}
pp = &p->next;
}
//return false because we did not find any matching node in the list
return false;
}
*текущий=голова->следующий; это нужно делать после того, как вы убедитесь, что голова не NULL