Home > front end >  malloc'd pointer inside struct that is passed by value
malloc'd pointer inside struct that is passed by value

Time:10-03

I am putting together a project in C where I must pass around a variable length byte sequence, but I'm trying to limit malloc calls due to potentially limited heap.

Say I have a struct, my_struct, that contains the variable length byte sequence, ptr, and a function, my_func, that creates an instance of my_struct. In my_func, my_struct.ptr is malloc'd and my_struct is returned by value. my_struct will then be used by other functions being passed by value: another_func. Code below.

  1. Is this "safe" to do against memory leaks provided somewhere on the original or any copy of my_struct when passed by value, I call my_struct_destroy or free the malloc'd pointer? Specifically, is there any way that when another_func returns, that inst.ptr is open to being rewritten or dangling?
  2. Since stackoverflow doesn't like opinion-based questions, are there any good references that discuss this behavior? I'm not sure what to search for.
typedef struct {
    char * ptr;
} my_struct;

// allocates n bytes to pointer in structure and initializes.
my_struct my_func(size_t n) {
    my_struct out = {(char *) malloc(n)};
    /* initialization  of out.ptr */

    return out;
}

void another_func(my_struct inst) {
    /* 
    do something using the passed-by-value inst 
    are there problems with inst.ptr here or after this function returns?
    */
}

void my_struct_destroy(my_struct * ms_ptr) {
    free(ms_ptr->ptr);
    ms_ptr->ptr = NULL;
}

int main() {
    my_struct inst = my_func(20);
    
    another_func(inst);
    
    my_struct_destroy(&inst);
}

CodePudding user response:

  1. I's safe to pass and return a struct containing a pointer by value as you did it. It contains a copy of ptr. Nothing is changed in the calling function. There would, of course, be a big problem if another_func frees ptr and then the caller tries to use it or free it again.

  2. Locality of alloc free is a best practice. Wherever possible, make the function that allocates an object also responsible for freeing it. Where that's not feasible, malloc and free of the same object should be in the same source file. Where that's not possible (think complex graph data structure with deletes), the collection of files that manage objects of a given type should be clearly identified and conventions documented. There's a common technique useful for programs (like compilers) that work in stages where much of the memory allocated in one stage should be freed before the next starts. Here, memory is only malloced in big blocks by a manager. From these, the manager allocs objects of any size. But it knows only one way to free: all at once, presumably at the end of a stage. This is a gcc idea: obstacks. When allocation is more complex, bigger systems implement some kind of garbage collector. Beyond these ideas, there are as many ways to manage C storage as there are colors. Sorry I don't have any pointers to references (pun intended :)

If you only have one variable-length field and its size doesn't need to be dynamically updated, consider making the last field in the struct an array to hold it. This is okay with the C standard:

typedef struct {
    ... other fields
    char a[1]; // variable length
} my_struct;

my_struct my_func(size_t n) {
  my_struct *p = malloc(sizeof *p   (n - 1) * sizeof p->a[0]);
  ... initialize fields of p
  return p;
}

This avoids the need to separately free the variable length field. Unfortunately it only works for one.

If you're okay with gcc extensions, you can allocate the array with size zero. In C 99, you can get the same effect with a[]. This avoids the - 1 in the size calculation.

  • Related