Скопируйте указатель на структуру в функции (связанный список) - PullRequest
0 голосов
/ 23 января 2020

Я хочу создать один связанный список без глобальных переменных. Я инициализировал первый элемент с NULL, а затем хотел скопировать первый элемент node в list_. Он копируется в функцию, но побочный эффект не работает. В моей основной функции значение по-прежнему NULL. Если я возвращаю структуру в функции add_element(), все работает нормально, но возможно ли, что l получает значение узла без изменения структуры функций и самой структуры?

#include <stdio.h>
#include <stdlib.h>

struct list {
        int value;
        struct list *next;
};


struct list *initialize(void)
{
    struct list * l = NULL;
    return l;
}

int add_element(struct list *list_, void *v)
{
    struct list *node = malloc(sizeof(struct list));
    node->value = *((int*)v);
    node->next = NULL;

    if(list_ == NULL)
    {
        list_ = node;
        printf("list_->value = %d\n", list_->value);    // correct copy
        return 0;
    }
    //TODO if not first then  add at end..
    return 0;
}

int main()
{
    struct list *l = initialize(); // l = NULL
    int i = 10;
    add_element(l,&i);
    if(l == NULL) printf("l == NULL!\n");
    printf("l->value = %d\n", l->value); // does not work, l is NULL
    return 0;
}

Ответы [ 2 ]

0 голосов
/ 23 января 2020

Для начала функция инициализации

struct list *initialize(void)
{
    struct list * l = NULL;
    return l;
}

не имеет большого смысла. Вы можете просто написать в main

struct list *l = NULL;

Или функция initialize может выглядеть как

inline struct list *initialize(void)
{
    return NULL;
}

Функция add_element имеет дело с копией переданного списка.

int add_element(struct list *list_, void *v);

Таким образом, любые изменения копии не влияют на исходный список. Также неясно, почему второй параметр имеет тип void * вместо типа int.

Вы должны передать список по ссылке на функцию.

Функция может выглядеть следующим образом

int add_element( struct list **head, int value )
{
    struct list *node = malloc( sizeof( struct list ) );
    int success = node != NULL;

    if ( success )
    {
        node->value = value;
        node->next = NULL;

        while ( *head != NULL ) head = &( *head )->next;

        *head = node;
    }

    return success;
}

и вызывается, например, как

int i = 10;

if ( !add_element( &l, i ) )
{
    puts( "Error: not enough memory." );
}

Вот демонстрационная программа

#include <stdio.h>
#include <stdlib.h>

struct list 
{
    int value;
    struct list *next;
};

static inline struct list * initialize( void )
{
    return NULL;
}

int add_element( struct list **head, int value )
{
    struct list *node = malloc( sizeof( struct list ) );
    int success = node != NULL;

    if ( success )
    {
        node->value = value;
        node->next = NULL;

        while ( *head != NULL ) head = &( *head )->next;

        *head = node;
    }

    return success;
}

void output( struct list *head )
{
    for ( ; head != NULL; head = head->next )
    {
        printf( "%d -> ", head->value );
    }

    puts( "NULL" );
}

int main(void) 
{
    struct list *head = initialize();

    const int N = 10;

    for ( int i = 0; i < N; i++ )
    {
        add_element( &head, i );
    }

    output( head );

    return 0;
}

Ее вывод

0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> NULL

Обратите внимание, что если к концу списка добавляется новый узел, то лучше определить список как двусторонний односвязный список.

0 голосов
/ 23 января 2020

Комментарий Кайлама указывает вам правильное направление.

Когда вы передаете указатель в C, значение указателя копируется в стек, и эта копия является значением, которое функция add_element() относится к. Когда вы изменяете значение указателя, вы изменяете копию, помещенную в стек, , а не исходный указатель .

Если вы хотите изменить исходный указатель (как если бы он был передан по ссылке а не по значению) вам нужно использовать двойной указатель.

Попробуйте этот вариант:

    int add_element(struct list **list_, void *v)
    {
        struct list *node = malloc(sizeof(struct list));
        node->value = *((int*)v);
        node->next = NULL;

        if(*list_ == NULL)  // dereferencing the double pointer will access the original pointer
        {
            *list_ = node;  // this will change the original pointer
            printf("(*list_)->value = %d\n", (*list_)->value);    // correct copy
            return 0;
        }
        //TODO if not first then  add at end..
        return 0;
    }

    int main()
    {
        struct list *l = initialize(); // l = NULL  
        int i = 10;
        add_element(&l,&i); // notice you are now passing the address of l instead of its value
        if(l == NULL) printf("l == NULL!\n");
        printf("l->value = %d\n", l->value); //should work now
        return 0;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...