Home > Software engineering >  Struct in header file, allocation in stack vs in heap?
Struct in header file, allocation in stack vs in heap?

Time:12-31

I'm learning C myself and stumble upon a "strange" behavior. I'm trying to replicate the list data structure of Python, using C.

For the header file listint.h, I have this:

#include "stdint.h"

#ifndef LISTINT_H
#define LISTINT_H

typedef struct { // simple list of int
    int ** list; // void pointer to the array of pointer
    uint32_t length; // size of the list
} ListInt;

void ListInt_print(ListInt * self);
void ListInt_append(ListInt * self, int * item); // add pointer of an item - pointer to the list

#endif

For the listint.c (compiled to liblistint.so), there is implementation of the ListInt_append:

#include "stdlib.h"
#include "stdio.h"
#include "listint.h"

void ListInt_append(ListInt * self, int * item) {
    if (self->list == NULL) { // uninitialized list
        int ** ptr;
        ptr = malloc(sizeof(int *));
        self->list = ptr;
        ptr[0] = item;
        self->length = 1;
        return;
    } else if (item != NULL) {
        int ** list_tmp; // temp list for realloc
        uint32_t new_size = self->length   1;
        int ** ptr;
        if ((list_tmp = realloc(self->list, sizeof(int *) * new_size)) == NULL) {
            return;
        };
        self->list = list_tmp;
        list_tmp[new_size - 1] = item;
        self->length = new_size;
        return;
    };
    return;
};

For the test file, I have tried two versions:

The first one, I declare two variables of type ListInt list this:

#include "stdio.h"
#include "stdlib.h"
#include "listint.h"

void main() {
    ListInt mlist1;
    ListInt mlist2;

    printf("mlist1.list address %p\n", mlist1.list);
    printf("mlist2.list address %p\n", mlist2.list);
    printf("mlist1.length value %d\n", mlist1.length);
    printf("mlist2.length value %d\n", mlist2.length);

}

Output:

mlist1.list address (nil)
mlist2.list address 0x7ffc3c4d3e20
mlist1.length value -1701232544
mlist2.length value 0

Moreover, if I added this:

int a = 5;
int b = 7;
ListInt_append(&mlist1, &a);
ListInt_append(&mlist2, &b);

The ListInt_append(&mlist2, &b); will return realloc() invalid pointer

The second one, declaring the two pointers:

ListInt * mlist1 = malloc(sizeof(ListInt));
ListInt * mlist2 = malloc(sizeof(ListInt));

The initial values of attributes:

mlist1.list address (nil)
mlist2.list address (nil)
mlist1.length value 0
mlist2.length value 0

Running the following worked as expected:

int a = 5;
int b = 7;
ListInt_append(&mlist1, &a);
ListInt_append(&mlist2, &b);

It seems I'm missing st very fundamental here. Plz pardon my lack of understanding. Thanks in advance.

CodePudding user response:

Objects defined with ListInt mlist1; and ListInt mlist2; inside functions are not given initial values. Their initial values are indeterminate, and the program behavior is generally not defined by the C standard when you attempt to use the values. (If the address of the object is taken, the program behavior becomes somewhat defined, but the value remains indeterminate.)

You can give them initial values with:

ListInt mlist1 = { 0, 0 }; // Initialize members in order.
ListInt mlist1 = { .list = 0, .length = 0; } // Initialize by member name.

Similarly, with malloc, no initial value is given. After allocation, you can give the structures values by assigning to their members, as with:

mlist1->list   = 0;
mlist1->length = 0;

Alternately, you can have the memory set to all zeros by using ListInt *mlist1 = calloc(1, sizeof *mlist1); instead of using malloc. That usually suffices, although technically it is not specified that all-zero bytes represents a null pointer, so the assignment method is more portable in theory.

If the objects were declared outside of a function or with static, as in static ListInt mlist1;, they would have static storage duration and would automatically be initialized with initial values of zero (including setting pointers to a null pointer value).

  •  Tags:  
  • c
  • Related