0

I want to craete a single linked list without gloabal variables. I initialized the first element with NULL and then wanted to copy the first element node to list_. It is copied in the function but the side effect isn´t working. In my main-function the value is still NULL. If I return the struct in the add_element()function all works fine but, is it possible that l gets the value of node without changing the functions structure and the struct itself?

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

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 2
    Does this answer your question? [Changing address contained by pointer using function](https://stackoverflow.com/questions/13431108/changing-address-contained-by-pointer-using-function) – kaylum Jan 22 '20 at 21:14

2 Answers2

0

kaylum's comment points you in the right direction.

When you pass a pointer in C, the pointer's value is copied to the stack, and this copy is the value that the add_element() function is referring to. When you alter the pointer's value, you are modifying the copy placed on the stack, not the original pointer.

If you want to alter the original pointer (as if it was passed by reference and not by value) you need to use a double pointer.

Try this variant:

    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;
    }
BlueStrat
  • 2,202
  • 17
  • 27
  • Thank you Sir, I can understand the problem. But the function structure ```int add_element(struct list *list_, void *v)``` is given. Is it somehow possible to modify the struct s1, which I put in the function? – langung Jan 23 '20 at 17:05
0

For starters the function initialize

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

does not make great sense. You can just write in main

struct list *l = NULL;

Or the function initialize can look like

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

The function add_element deals with a copy of the passed list.

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

So any changes of the copy do not influence on the original list. Also it is unclear why the second parameter has the type void * instead of the type int.

You have to pass the list by reference to the function.

The function can look the following way

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

and called for example like

int i = 10;

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

Here is a demonstrative program

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

Its output is

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

Pay attention to that if a new node is appended to the tail of the list then it is better to define the list as a two-sided singly-linked list.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Also thanks, the problem I had is understandable now, but the function structure is given so is it posible to modify the struct I give to the function, when I only put in the ```struct list *head```? – langung Jan 23 '20 at 17:08
  • @langung If the parameter of the function shall be struct list *head then the only approach is to return the modified pointer from the function and assign it to the original pointer head. – Vlad from Moscow Jan 23 '20 at 17:13
  • And is it posibble to allocate the memory in the initialize() function and just call the initialize() function in the add_element() function and change/initalize only the value in the add_element() function? – langung Jan 23 '20 at 17:42
  • @langung You can do as you like provided that your code is correct. – Vlad from Moscow Jan 23 '20 at 17:43