Home > Enterprise >  How to correctly initialise a struct in C
How to correctly initialise a struct in C

Time:09-22

I'm working on a small project in C and I wan't to allocate structs in a function and add them to an array of structs.

For some reason, when I go to print the contents of the array of structs, I appear to start printing from unallocated memory.

A minimum working example is provided below:

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

// This struct simply stores a list and its size
struct list {
    int* values;
    size_t size;
};

// This function initialises an array of lists
// of size len, which each list (i.e. list.values)
// having a size of list_len
// `lists` is an array of pointers to lists
void list_init(struct list** lists, size_t len, size_t list_len) {
    for (size_t i = 0; i < len; i  ) {
        struct list list;
        list.values = malloc(sizeof(int) * list_len);
        list.size = list_len;
        lists[i] = &list;
    }
}

void main() {
    int len = 3;
    struct list* lists[len];
    list_init(lists, len, 5);

    // Print lists
    for (size_t i = 0; i < len; i  ) {
        printf("list %zu: ", i);
        printf("size: %zu\n", lists[i]->size);
        for (size_t j = 0; j < 5; j  ) { // Using 5 instead of lists[i]->size for obvious reasons
            printf("%d ", lists[i]->values[j]);
        }
        printf("\n");
    }
}

The output I would expect from this is:

list 0: size: 5
0 0 0 0 0
list 1: size: 5
0 0 0 0 0
list 2: size: 5
0 0 0 0 0

but instead I get:

list 0: size: 5
0 0 0 0 0 
list 1: size: 140727488332736
0 0 0 0 0 
list 2: size: 140727488332736
0 0 0 0 0

which is a pretty clear sign that I'm accessing memory that I'm not supposed to.

I noticed that rather than declaring the list with struct list list;, if I declare the list by allocating memory to a pointer with struct list* list = malloc(sizeof(struct list)); the program gives the expected output. Why is that? If I want to create the object, rather than a pointer, how can I do that properly.


P.S.: I am aware that I could just initialise list as a pointer. This question is mostly asking why can't I initialise it as an object

CodePudding user response:

You save the references to the same local variable and it is an UB. Also malloced memory is lost. Your main is also wrong.

I would do it this way (calloc is used as in main you print not initialized allocated memory):

typedef struct list {
    size_t size;
    int values[];
}list;

list **list_init(list **array, size_t size, size_t list_len) 
{
    list **wrk;
    if(!array) wrk = malloc(sizeof(*wrk) * size);
    else wrk = array; 
    if(wrk)
        for (size_t i = 0; i < size; i  ) {
            list *list = calloc(1, sizeof(*list)   list_len * sizeof(list -> values[0]));
            /* check for allocation errors!!!! */
            list -> size  = list_len;
            wrk[i] = list;
        }
    return wrk;
}

int main(void) {
    size_t len = 3;
    list **lists;
    
    /* if you pass NULL it will create the list of lists itself */
    lists = list_init(NULL, len, 5);
    /* check for allocation errors!!!! */

    // Print lists
    for (size_t i = 0; i < len; i  ) {
        printf("list %zu: ", i);
        printf("size: %zu\n", lists[i]->size);
        for (size_t j = 0; j < 5; j  ) { // Using 5 instead of lists[i]->size for obvious reasons
            printf("%d ", lists[i]->values[j]);
        }
        printf("\n");
    }
    for (size_t i = 0; i < len; i  ) free(lists[i]);
    free(lists);
}

https://godbolt.org/z/9TPe1sM1a

CodePudding user response:

These statements within the function list_init

struct list list;
//...
lists[i] = &list;

does not make a sense because the local object list will not be alive after exiting the function. So you will have an array of invalid pointers of the type struct list *.

You need to allocate dynamically each object of the type struct list that will be pointed to by an element of the array.

The function can be declared and defined for example the following way

size_t list_init( struct list **lists, size_t len, size_t list_len ) 
{
    size_t count = 0;

    for ( size_t i = 0; i < len; i   ) 
    {
        lists[i] = malloc( sizeof( struct list ) );
        
        if ( lists[i] != NULL )
        {
              count;
            
            lists[i]->size = 0;

            lists[i]->values = malloc( sizeof( int ) * list_len );
            
            if ( lists[i]->values != NULL ) lists[i]->size = list_len;
        }
    }

    return count;
}

Also as the function does not initialize the allocated array pointed to by the data member values then this loop in main

    for (size_t j = 0; j < 5; j  ) { // Using 5 instead of lists[i]->size for obvious reasons
        printf("%d ", lists[i]->values[j]);
    }

will invoke undefined behavior.

You could zero initialize the arras by using calloc instead of malloc. For example

lists[i]->values = calloc( list_len, sizeof( int ) );

And pay attention to that according to the C Standard the function main without parameters shall be declared like

int main( void )
  • Related