Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should a C function return newly allocated memory?

In a response elsewhere, I found the following snippet:

In general it is nicer in C to have the caller allocate memory, not the callee - hence why strcpy is a "nicer" function, in my opinion, than strdup.

I can see how this is a valid pattern, but why might it be considered nicer? Are there advantages to following this pattern? Or not?

example

Recently I've written a fair amount of code that looks something like:

struct foo *a = foo_create();
// do something with a
foo_destroy(a);

If foo is a anything more than a flat structure, then I figured I could put all my initialization in one step. Also, assume the struct should be on the heap. Why might it be better form to do something like:

struct foo *a = malloc(sizeof(foo));
foo_init(a);
// do something with a
foo_destroy(a)
like image 808
Willi Ballenthin Avatar asked Dec 29 '09 01:12

Willi Ballenthin


2 Answers

Whenever you want an opaque structure and don't want to expose its internals in the header file. Your foo_create() example illustrates this.

Another example is the Windows API. E.g. CreateWindow gives you a HWND. You have no idea what the actual WND structure looks like and can't touch its fields.

Same with kernel object handles. E.g. CreateEvent gives a HANDLE. You can only manipulate it with the well-defined API, and close it with CloseHandle().

Re:

struct foo *a = malloc(sizeof(foo));

This requires you to define struct foo in a header, and hence expose its internals. If you want to change it down the track, you risk breaking existing code that (incorrectly) relied on its members directly.

like image 158
Alex Budovski Avatar answered Oct 23 '22 21:10

Alex Budovski


The main advantage of having the caller allocate the memory is that it simplifies the interface, and it's completely unambiguous that the caller owns the memory. As your create/destroy example shows, the simplification is not very great.

I prefer the create/destroy convention established by Dave Hanson in C Interfaces and Implementations:

struct foo *foo_new(...);   // returns result of malloc()
void foo_free(struct foo **foop); // free *foop's resources and set *foop = NULL

You follow the convention thus:

struct foo *a = foo_new();
...
foo_free(&a);
// now `a` is guaranteed to be NULL

This convention makes it a little less likely you will leave a dangling pointer.

like image 37
Norman Ramsey Avatar answered Oct 23 '22 20:10

Norman Ramsey