Mail.ruПочтаМой МирОдноклассникиВКонтактеИгрыЗнакомстваНовостиКалендарьОблакоЗаметкиВсе проекты

Списки в Си

Hidden Hidden Ученик (112), на голосовании 4 месяца назад
Доброго времени суток.
У меня есть узел списка
 typedef struct l_node {
line* data;
struct l_node* next;
struct l_node* prev;
} l_node;
И сам список список:
 typedef struct {
l_node* first;
l_node* last;
} list;
Инициализируется список таким образом:
 void Lcreate(list **lst) {
(*lst) = (list *)malloc(sizeof(list));
(*lst)->first = (*lst)->last = (l_node *)malloc(sizeof(l_node));
(*lst)->first->next = (*lst)->last;
(*lst)->last->prev = (*lst)->first;
(*lst)->first->prev = NULL;
(*lst)->last->next = NULL;
}
Я хочу добавить новый элемент в список. Для этого я сначала выделяю под него память:
 l_node *t = (l_node *)malloc(sizeof(l_node)); 
Затем записываю необходимые данные в новый элемент:
 t->data = ln;
t->next = lst->last;
t->prev = lst->last->prev;
Затем присваиваю предпоследнему элементу указатель на новый элемент:
 lst->last->prev->next = t; 
Однако на этом шаге программа прекращает свою работу, не выводя никаких ошибок. Также стоит учитывать, что на данном этапе в списке уже есть один элемент, ведь добавление элемента в пустой список обрабатывается отдельно:
 t->data = ln; 
t->next = lst->last;
t->prev = NULL;
lst->first = t;
В чём проблема? Почему это не работает?
Голосование за лучший ответ
Татьяна Просветленный (36384) 5 месяцев назад
Давайте разберем ваш код и постараемся найти, где может возникать ошибка.

Для начала, структура list и функция Lcreate выглядят корректными:
 typedef struct l_node { 
line* data;
struct l_node* next;
struct l_node* prev;
} l_node;

typedef struct {
l_node* first;
l_node* last;
} list;

void Lcreate(list **lst) {
(*lst) = (list *)malloc(sizeof(list));
(*lst)->first = (*lst)->last = (l_node *)malloc(sizeof(l_node));
(*lst)->first->next = (*lst)->last;
(*lst)->last->prev = (*lst)->first;
(*lst)->first->prev = NULL;
(*lst)->last->next = NULL;
}
Теперь посмотрим на функцию добавления элемента в список. Здесь потенциальные проблемы могут быть в использовании указателей.
Код функции, которая добавляет элемент в список:
 void Ladd(list *lst, line *ln) { 
l_node *t = (l_node *)malloc(sizeof(l_node));
if (!t) {
// Обработка ошибки malloc
fprintf(stderr, "Memory allocation failed\n");
return;
}

t->data = ln;
t->next = lst->last;
t->prev = lst->last->prev;

if (lst->last->prev != NULL) {
lst->last->prev->next = t;
}
lst->last->prev = t;

if (lst->first == lst->last) {
// Это значит, что список был пустой до добавления элемента
lst->first = t;
}
}
Обратите внимание, что при добавлении первого элемента в список через функцию Lcreate, структура инициализируется так, что lst->first и lst->last указывают на один и тот же узел. Это означает, что для корректной работы кода добавления элементов, нам нужно правильно обрабатывать первый элемент и не забывать обновлять указатели first и last.

Проблема в вашем коде могла возникнуть из-за неверного состояния указателей next и prev у узлов списка, особенно когда добавляется первый элемент. Проверка lst->first == lst->last поможет понять, добавляете ли вы первый элемент или нет, и корректно обновить указатели.

Если список был пустым до добавления элемента, то нужно также правильно обновить lst->first:
 if (lst->first == lst->last) { 
lst->first = t;
}
Таким образом, функция добавления элемента будет правильно работать как для пустого, так и для непустого списка.
Hidden HiddenУченик (112) 5 месяцев назад
Случай с добавлением элемента в пустой список отдельно обрабатывается, ошибка возникает при добавлении элемента в уже не пустой список.
Вот полной код функции:
 bool Ladd(list *lst, line* ln) { 
l_node *t = (l_node *)malloc(sizeof(l_node));
if (!t)
return false;
t->data = ln;
t->next = lst->last;
if (Lempty(lst)) {
t->prev = NULL;
lst->first = t;
} else {
t->prev = lst->last->prev;
lst->last->prev->next = t;
}
return true;
}
Татьяна Просветленный (36384) Hidden Hidden, Правильная версия функции будет выглядеть так:
 bool Ladd(list *lst, line* ln) {  
    l_node *t = (l_node *)malloc(sizeof(l_node));  
    if (!t)  
        return false;  
    t->data = ln;  
    t->next = lst->last;  
    if (Lempty(lst)) {  
        t->prev = NULL;  
        lst->first = t;  
    } else {  
        t->prev = lst->last->prev;  
        lst->last->prev->next = t;  
    }  
    lst->last->prev = t; // Эта строка добавлена 
    return true;  
} 
 
ТатьянаПросветленный (36384) 5 месяцев назад
Также убедитесь, что функция Lempty правильно определяет, пуст ли список:
 bool Lempty(list *lst) { 
return (lst->first == lst->last);
}
V̲i̲s̲t̲a̲s̲t̲e̲r̲ Искусственный Интеллект (264224) 5 месяцев назад
 попробуй так 
 #include  
#include

// Структура узла списка
typedef struct l_node {
void* data;
struct l_node* next;
struct l_node* prev;
} l_node;

// Структура списка
typedef struct {
l_node* first;
l_node* last;
} list;

// Инициализация списка
void Lcreate(list **lst) {
*lst = (list *)malloc(sizeof(list));
(*lst)->first = NULL;
(*lst)->last = NULL;
}

// Добавление элемента в конец списка
void Ladd(list *lst, void *data) {
l_node *new_node = (l_node *)malloc(sizeof(l_node));
new_node->data = data;
new_node->next = NULL;
new_node->prev = NULL;

if (lst->first == NULL) {
// Список пустой
lst->first = new_node;
lst->last = new_node;
} else {
// Список не пустой
new_node->prev = lst->last;
lst->last->next = new_node;
lst->last = new_node;
}
}

// Удаление узла из списка
void Lremove(list *lst, l_node *node) {
if (lst->first == NULL || node == NULL) return;

if (node == lst->first) {
lst->first = node->next;
if (lst->first != NULL) {
lst->first->prev = NULL;
} else {
lst->last = NULL;
}
} else if (node == lst->last) {
lst->last = node->prev;
if (lst->last != NULL) {
lst->last->next = NULL;
} else {
lst->first = NULL;
}
} else {
node->prev->next = node->next;
node->next->prev = node->prev;
}

free(node);
}

// Освобождение памяти списка
void Ldestroy(list *lst) {
l_node *current = lst->first;
l_node *next_node;

while (current != NULL) {
next_node = current->next;
free(current);
current = next_node;
}

free(lst);
}

int main() {
list *myList;
Lcreate(&myList);

int data1 = 1, data2 = 2, data3 = 3;
Ladd(myList, &data1);
Ladd(myList, &data2);
Ladd(myList, &data3);

// Удаление среднего узла (data2)
Lremove(myList, myList->first->next);

// Освобождение памяти списка
Ldestroy(myList);

return 0;
}
D PМудрец (17850) 5 месяцев назад
Все хорошо кроме одного: в функцию void Ladd(list *lst, void *data) с высокой вероятностью будет передан указатель на выделенные из памяти данные. В результате после выполнения функции void Ldestroy(list *lst) мы получим утечку данных. Я бы добавил в структуру list поле с флагом, который заполнялся бы при создании списка и указывал, требуется ли при удалении элемента удалять сохраненные в нем данные. И еще поле счетчика общего количества элементов так как это часто бывает нужно.
Похожие вопросы