bgarrood
bgarrood

Reputation: 487

Best way to structure C code where malloc used inside a function but free in calling function

I have a function which populates dynamic arrays based on contents of a binary file. I am using malloc to size the array of strings inside the function, then return the pointer and size information back to the calling function. Since it is an array of strings I am using an array of pointers, so calling malloc for the array pointer, then malloc for each member.

i.e. in my function I have this code block. The function returns p_count and p_tokens to the caller.

    // Allocate memory:
    *p_tokens = malloc(*p_count * sizeof(char *));
    for (int i = 0; i < (int)*p_count; i++) {
        (*p_tokens)[i] = malloc(TOKEN_LENGTH);
    }

This makes use of free from calling code necessary, but of course gets a bit messy. What is the best way to handle this scenario? Should I make a separate function to free all pointers generated within my original function? Should I just deal with a cluster of free loops at the end of my calling code, or am I going about the whole thing incorrectly?

Upvotes: 1

Views: 97

Answers (3)

sanaris
sanaris

Reputation: 145

Ideally you should control the status of your "strings" somewhere. Structure of: pointers + flags for existence + flags for initialization + flags for access.

struct arrstring {
    p_string * Strings;
    // flags of allocation
    p_exist * ExistFlag;
    // flags tracking initialization with values
    p_init * InitStrings;
    // flags allowing access/lock
    p_access * AccessF;
    p_mutex * LockStrings;
}

This way you could create all strings with one call to 'calloc()', but initialize them later.

PS.

Here at sizeof(char *) you take size of pointer, I don't know your intentions but this looks like a misunderstanding. You can take ahead the maximum size of your string like 512 byte, and allocate it with malloc(MAX_SIZE*sizeof(char)).

Also using 'char' for strings is kind of deprecated feature. Good for testing purposes, but there is actual characters today, and 'char' is used as only acronym of 'byte' (it works because UTF-8 is widely accepted and smallest UTF-8 sized values picked coincidentally with ASCII).

PS2. The C17 standard is confirming that "uchar.h" and "wchar.h" are widely accepted and supported by C environments. So users could deal with characters accordingly if it goes beyond 'char' type.

PS3. Well, about the C standards about characters. C always were "silent" when it comes to characters and various systems decide it differently. So the way how C works with strings is named as "C strings" to distinguish it. In "C strings" the length of strings is always measured in "char" or "wchar_t" which are fixed size, even if the string itself is variable-width encoded, this could lead to problems because long width characters will be interpreted as few characters. But the size of "char" will always be fixed itself, on some machines it was 6 bit, on some machines it was 12 bit. Now it is mostly 8 bit, so most of the times programmers are referring to it as "integer type", and there is common ground when programmers suppose that char=byte. Well it reflects by itself the evolution of term of "byte" itself.

Upvotes: 0

chqrlie
chqrlie

Reputation: 144780

Writing a free_token_array(token *array, size_t count) that would free the tokens and the array in the same module as your allocation function makes sense. This way you can easily keep them in sync if you change allocation strategies.

Also note that you should avoid casting *p_count in the allocation loop, just write:

    for (size_t i = 0; i < *p_count; i++)

Upvotes: 2

vonbrand
vonbrand

Reputation: 11791

You must make sure the malloc()s are paired with the free()s. Where you write them is quite irrelevant, the really important point is that the code is structured so that it is easy to check (i.e., hard(er) to get wrong) that they do balance. For example, around the idea of "create (and allocate), use, consume (and free)".

Look for debugging packages, there are instrumented libraries that check specifically for memory management bugs.

Another way to handle this (at some performance cost) is to use some garbage collecting library, like the Boehm garbage collector for C and C++ (a detailed example is given in "Garbage Collection in C Programs" by Insolvible,Linux Journal, aug 2003). The garbage collector is available on most Linux distributions, being open source it can be built and installed on most systems.

Upvotes: 2

Related Questions