#c #string #linked-list
#c #строка #связанный список
Вопрос:
Предположим, у меня есть следующая структура:
struct _node {
int id;
char *name;
struct node *next;
};
Я хочу написать функцию, которая просматривает linkedlist и ищет структуры, член name которых содержит определенную подстроку, и добавляет эти структуры в новый linkedlist и возвращает новый linkedlist.
Я попытался сделать это с помощью strstr, но в какой-то момент я получаю бесконечный цикл, и я не могу точно понять, почему происходит бесконечный цикл.
Вот моя функция:
struct _node *containSubstr(struct _node *head, char needle[]) {
if (head == NULL) {
printf("BLOOP BLEEP BOOP BEEP.n");
exit(EXIT_FAILURE);
}
struct _node *curr = head;
struct _node *returnHead = createEmptyNode();
while (curr != NULL) {
char haystack[strlen(curr->name)];
strcpy(haystack, curr->name);
strToLower(needle);
strToLower(haystack);
if (strstr(haystack, needle) != NULL) {
// this is where I get the infinite loop
append(returnHead, curr);
}
curr = curr->next;
}
return returnHead;
}
Функции append и createEmptyNode протестированы и работают нормально.
Я несколько раз просматривал логику, и я думаю, что это должно сработать. Я заполнил свой код инструкциями print, и кажется, что после того, как он находит все узлы, содержащие подстроку, он продолжает повторять последний узел и переходит в бесконечный цикл.
Вот моя функция добавления:
void append(struct _node *head, struct _node *newNode) {
if (head == NULL) {
printf("BLOOP BLEEP BOOP BEEP.n");
exit(EXIT_FAILURE);
}
struct _node *curr = head;
if(curr == NULL) {
head = newNode;
}
else {
while(curr->next != NULL) {
curr = curr->next;
}
curr->next = newNode;
}
}
Комментарии:
1.
The C library function size_t strlen(const char *str) computes the length of the string str up to, but not including the terminating null character.
2. Я исправил это, но все равно получаю бесконечный цикл.
3. указывает ли ваш последний элемент на
null
, что является следующим очевидным вопросом.4. покажите свой
createEmptyNode
5. В
append
функции второйif
оператор никогда не будет выполнен.
Ответ №1:
Представьте такой случай: у вас есть список L, который содержит всего 2 узла (A и B) в вашем связанном списке. Представьте, что оба узла A и B содержат подстроку, которую вы ищете:
Исходный список L:
A-> B-> NULL
Итак, после первой итерации ваш новый список L2 должен выглядеть следующим образом:
A-> NULL
Но в вашей функции добавления вы не создаете глубокую копию нового узла. Следовательно, ваш новый список L2 выглядит следующим образом:
Empty_node->A-> B-> NULL
На следующем шаге вы переходите к узлу B. Итак, вы берете свой L2 и добавляете туда B. Ваш L2 после второй итерации выглядит следующим образом:
Empty_node->A->B-> B (B указывает на себя).
Поскольку вы не создаете глубоких копий, вы фактически всегда работаете со списком L, и когда вы добавляете узел B к тому, что вы считаете списком L2, вы фактически добавляете B к L, а затем B указывает на себя ( curr->next = newNode;
в вашей append
функции). Итак, на следующей итерации вы снова спрашиваете, содержит ли B искомую строку.
Заключение
При создании нового списка необходимо создавать глубокие копии.
В текущей настройке ваша append
функция изменяет исходный список.
Комментарии:
1. Привет, для меня это имеет смысл. Не могли бы вы, пожалуйста, подробнее рассказать об этом. Как мне инициализировать новый список? Как я могу сделать его глубоким? В настоящее время я просто переназначаю его, а затем устанавливаю для его элементов значение NULL.
2. Глубокое копирование узла означает, что вы создаете новый узел, который содержит точно такие же значения, что и исходный узел. Итак, выделите память для узла, выделите память для
name
внутри этого узла и скопируйте значения из исходного узла в новый.3. Я скопировал узел curr в новый узел и malloced его перед добавлением, и это волшебным образом сработало. Все еще не понимаю, почему. Все остальное точно такое же.
4. @user6005857 Я рад, что это работает, но вы должны попытаться понять, что было не так с вашим исходным кодом. Для начала вы можете каким-то образом распечатать свои связанные списки (как исходный, так и тот, который должен содержать только нужные узлы), чтобы посмотреть, как это выглядит после каждой итерации. Я хотел бы объяснить это лучше.