nsg
nsg

Reputation: 10580

Return a pointer to a dynamically-allocated struct vs. requiring allocated memory from the calling function?

In C, it is possible for functions to return pointers to memory that that function dynamically-allocated and require the calling code to free it. It's also common to require that the calling code supplies a buffer to a second function, which then sets the contents of that buffer. For example:

struct mystruct {
   int a;
   char *b;
};


struct mystruct *get_a_struct(int a, char*b)
{
    struct mystruct *m = malloc(sizeof(struct mystruct));
    m->a = a;
    m->b = b;

    return m;
}

int init_a_struct(int a, char*b, struct mystruct *s)
{
    int success = 0;
    if (a < 10) {
        success = 1;
        s->a = a;
        s->b = b;
    }

    return success;
}

Is one or the other method preferable? I can think of arguments for both: for the get_a_struct method the calling code is simplified because it only needs to free() the returned struct; for the init_a_struct method there is a very low likelihood that the calling code will fail to free() dynamically-allocated memory since the calling code itself probably allocated it.

Upvotes: 5

Views: 2532

Answers (4)

lvella
lvella

Reputation: 13413

I agree with other answers that passing the allocated struct is preferred, but there is one situation where returning a pointer may be preferred.

In case you need to explicitly free some resource at the end (close a file or socket, or free some memory internal to the struct, or join a thread, or something else that would require a destructor in C++), I think it may be better to allocate internally, then returning the pointer.

I think it so because, in C, it means some kind of a contract: if you allocate your own struct, you shouldn't have to do anything to destroy it, and it be automatically cleared at the end of the function. On the other hand, if you received some dynamically allocated pointer, you feel compelled to call something to destroy it at the end, and this destroy_a_struct function is where you will put the other cleanup tasks needed, alongside free.

Upvotes: 0

AZoo
AZoo

Reputation: 51

It depends on the specific situation but in general supplying the allocated buffer seems to be preferable.

As mentioned by Jim, DLLs can cause problems if called function allocates memory. That would be the case if you decide to distribute the code as a Dll and get_a_struct is exported to/is visible by the users of the DLL. Then the users have to figure out, hopefully from documentation, if they should free the memory using free, delete or other OS specific function. Furthermore, even if they use the correct function to free the memory they might be using a different version of the C/C++ runtime. This can lead to bugs that are rather hard to find. Check this Raymond Chen post or search for "memory allocation dll boundaries". The typical solution is export from the DLL your own free function. So you will have the pair: get_a_struct/release_a_struct.

In the other hand, sometimes only the called function knows the amount of memory that needs to be allocated. In this case it makes more sense for the called function to do the allocation. If that is not possible, say because of the DLL boundary issue, a typical albeit ugly solution is to provide a mechanism to find this information. For example in Windows the GetCurrentDirectory function will return the required buffer size if you pass 0 and NULL as its parameters.

Upvotes: 4

Mats Petersson
Mats Petersson

Reputation: 129314

The "pass an pointer in is preferred", unless it's absolutely required that every object is a "new object allocated from the heap" for some logistical reason - e.g. it's going to be put into a linked list as a node, and the linked-list handler will eventually destroy the elements by calling free - or some other situation where "all things created from here will go to free later on).

Note that "not calling malloc" is always the preferred solution if possible. Not only does calling malloc take some time, it also means that some place, you will have to call free on the allocated memory, and every allocated object takes several bytes (typically 12-40 bytes) of "overhead" - so allocating space for small objects is definitely wasteful.

Upvotes: 1

user2553780
user2553780

Reputation: 151

I think that providing the already allocated struct as an argument is preferable, because in most cases you wouldn't need to call malloc/calloc in the calling code, and therefore worrying about free'ing it. Example:

int init_struct(struct some_struct *ss, args...)
{
    // init ss
}

int main()
{
    struct some_struct foo;
    init_struct(&foo, some_args...);
    // free is not needed
} 

Upvotes: 1

Related Questions