Арифметика указателя С++ для связанных списков

Я только начинаю самообучаться С++, и в качестве игрушечной проблемы я пытаюсь сделать следующее: учитывая связанный список, я хочу сохранить все узлы, которые есть даже в новом списке, и вернуть этот новый список. Для контекста, я родом из 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)

не работает?

Любая помощь будет оценена!

С помощью node new_node = ...; вы определяете локальную переменную. Переменная, время жизни которой закончится, когда закончится текущий блок, то есть переменная перестанет существовать. Указатель, который вы получаете с помощью &new_node, станет бесполезным, и любая попытка разыменования этого указателя приведет к неопределенному поведению. Пожалуйста, обновите свои учебники или учебные пособия о масштабах и продолжительности жизни.

Some programmer dude 21.11.2022 12:39

Спасибо за ответ! Это мне очень помогло. Кажется, в тот момент, когда я изменил следующую строку - 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, мы гарантируем, что значение, на которое указывает указатель, всегда действительно?

mndl 21.11.2022 12:47

@AvishekMondal Не используйте malloc в программе на C++, вместо этого используйте new

john 21.11.2022 12:50

@AvishekMondal C++ — сложный язык со множеством ловушек для неосторожных. Понятно, что вы учитесь без надлежащего справочного материала. Я предлагаю вам купить хорошую книгу

john 21.11.2022 12:51

Также ваш malloc неверен, он должен быть sizeof(node), а не sizeof(node*). Как я уже сказал, вам действительно нужен правильный учебный материал.

john 21.11.2022 12:53
Шаблоны Angular PrimeNg
Шаблоны Angular PrimeNg
Как привнести проверку типов в наши шаблоны Angular, использующие компоненты библиотеки PrimeNg, и настроить их отображение с помощью встроенной...
Создайте ползком, похожим на звездные войны, с помощью CSS и Javascript
Создайте ползком, похожим на звездные войны, с помощью CSS и Javascript
Если вы веб-разработчик (или хотите им стать), то вы наверняка гик и вам нравятся "Звездные войны". А как бы вы хотели, чтобы фоном для вашего...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Начала с розового дизайна
Начала с розового дизайна
Pink Design - это система дизайна Appwrite с открытым исходным кодом для создания последовательных и многократно используемых пользовательских...
Шлюз в PHP
Шлюз в PHP
API-шлюз (AG) - это сервер, который действует как единая точка входа для набора микросервисов.
14 Задание: Типы данных и структуры данных Python для DevOps
14 Задание: Типы данных и структуры данных Python для DevOps
проверить тип данных используемой переменной, мы можем просто написать: your_variable=100
0
5
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не можете выполнять динамическое выделение, взяв адреса локальных переменных. Когда область видимости выходит, локальная переменная уничтожается, и у вас остается указатель на несуществующий объект (известный как висячий указатель).

В вашем коде есть эта проблема здесь

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;
}

Другие вопросы по теме