Я только начинаю самообучаться С++, и в качестве игрушечной проблемы я пытаюсь сделать следующее: учитывая связанный список, я хочу сохранить все узлы, которые есть даже в новом списке, и вернуть этот новый список. Для контекста, я родом из Python.
У меня есть следующая программа -
#include <iostream>
using namespace std;
struct node
{
unsigned val;
struct node *next;
};
node *even_nodes(node **root)
{
node *new_list_head = NULL;
node *new_list_runner = NULL;
node *runner = *root;
while (runner != NULL)
{
if (new_list_head != NULL){
printf("OUTSIDE LOOP new_list_head.val = %d\n", new_list_head->val);
}
if (runner->val % 2 == 0)
{
cout << runner->val << endl;
node new_node = {.val = runner->val, .next = NULL};
if (new_list_head == NULL)
{
printf("new_list_head is NULL!\n");
new_list_head = &new_node;
new_list_runner = &new_node;
printf("after allocation. new_list_head.val = %d\n", new_list_head->val);
}
else
{
printf("new_list_head is NOT NULL! new_list_head.val = %d\n", new_list_head->val);
new_list_runner->next = &new_node;
new_list_runner = new_list_runner->next;
printf("after allocation. new_list_head.val = %d\n", new_list_head->val);
}
}
runner = runner->next;
}
printf("new_list_head val = %d\n", new_list_head->val);
return new_list_head;
};
void add_data(node **root, int new_data)
{
node *new_node = (node *)malloc(sizeof(node *));
new_node->val = new_data;
new_node->next = (*root);
(*root) = new_node;
}
void print_list(node *root)
{
node *head = root;
while (head != NULL)
{
printf("%d -> ", head->val);
head = head->next;
}
printf("END\n");
};
int main()
{
node *head = NULL;
add_data(&head, 19);
add_data(&head, 18);
add_data(&head, 3);
add_data(&head, 4);
add_data(&head, 1);
printf("Initial list:\n");
print_list(head);
node *new_list = even_nodes(&head);
printf("New list of even numbers: \n");
print_list(new_list);
return 0;
}
Вывод следующий -
Initial list:
1 -> 4 -> 3 -> 18 -> 19 -> END
4
new_list_head is NULL!
after allocation. new_list_head.val = 4
OUTSIDE LOOP new_list_head.val = 4
OUTSIDE LOOP new_list_head.val = 4
18
new_list_head is NOT NULL! new_list_head.val = 18
after allocation. new_list_head.val = 18
OUTSIDE LOOP new_list_head.val = 18
new_list_head val = 18
New list of even numbers:
[1] segmentation fault
Я не понимаю, почему new_list_head
также меняется с new_list_runner
? Почему мой new_list_head
указывает на последний элемент нового списка, а не на первый?
Кроме того, почему возникает ошибка ошибки сегмента? В методе print_list
почему охрана
while (head != NULL)
не работает?
Любая помощь будет оценена!
Спасибо за ответ! Это мне очень помогло. Кажется, в тот момент, когда я изменил следующую строку - cpp node new_node = {.val = runner->val, .next = NULL};
на cpp node *new_node = (node *)malloc(sizeof(node *)); new_node->val = runner->val; new_node->next = NULL;
, похоже, она работает. Однако я не могу найти объяснение, почему это работает — потому что, выполняя malloc
, мы гарантируем, что значение, на которое указывает указатель, всегда действительно?
@AvishekMondal Не используйте malloc
в программе на C++, вместо этого используйте new
@AvishekMondal C++ — сложный язык со множеством ловушек для неосторожных. Понятно, что вы учитесь без надлежащего справочного материала. Я предлагаю вам купить хорошую книгу
Также ваш malloc неверен, он должен быть sizeof(node)
, а не sizeof(node*)
. Как я уже сказал, вам действительно нужен правильный учебный материал.
Вы не можете выполнять динамическое выделение, взяв адреса локальных переменных. Когда область видимости выходит, локальная переменная уничтожается, и у вас остается указатель на несуществующий объект (известный как висячий указатель).
В вашем коде есть эта проблема здесь
node new_node = {.val = runner->val, .next = NULL}; // local variable
if (new_list_head == NULL)
{
new_list_head = &new_node; // BAD
new_list_runner = &new_node; // BAD
}
else
{
new_list_runner->next = &new_node; // BAD
new_list_runner = new_list_runner->next;
}
вместо этого вы должны использовать new
для выделения новых узлов. Объекты, созданные с помощью new
, не уничтожаются, пока вы их delete
не уничтожите.
node* new_node = new node{runner->val, NULL};
if (new_list_head == NULL)
{
new_list_head = new_node;
new_list_runner = new_node;
}
else
{
new_list_runner->next = new_node;
new_list_runner = new_list_runner->next;
}
С помощью
node new_node = ...;
вы определяете локальную переменную. Переменная, время жизни которой закончится, когда закончится текущий блок, то есть переменная перестанет существовать. Указатель, который вы получаете с помощью&new_node
, станет бесполезным, и любая попытка разыменования этого указателя приведет к неопределенному поведению. Пожалуйста, обновите свои учебники или учебные пособия о масштабах и продолжительности жизни.