Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C API design: Who should allocate? [closed]

What is the proper/preferred way to allocate memory in a C API?

I can see, at first, two options:

1) Let the caller do all the (outer) memory handling:

myStruct *s = malloc(sizeof(s)); myStruct_init(s);  myStruct_foo(s);  myStruct_destroy(s); free(s); 

The _init and _destroy functions are necessary since some more memory may be allocated inside, and it must be handled somewhere.

This has the disadvantage of being longer, but also the malloc can be eliminated in some cases (e.g., it can be passed a stack-allocated struct:

int bar() {     myStruct s;     myStruct_init(&s);      myStruct_foo(&s);      myStruct_destroy(&s); } 

Also, it's necessary for the caller to know the size of the struct.

2) Hide mallocs in _init and frees in _destroy.

Advantages: shorter code, since the functions are going to be called anyway. Completely opaque structures.

Disadvantages: Can't be passed a struct allocated in a different way.

myStruct *s = myStruct_init();  myStruct_foo(s);  myStruct_destroy(foo); 

I'm currently leaning for the first case; then again, I don't know about C API design.

like image 430
Tordek Avatar asked Jul 21 '10 04:07

Tordek


People also ask

Is C dynamically allocated?

“calloc” or “contiguous allocation” method in C is used to dynamically allocate the specified number of blocks of memory of the specified type.

How dynamic memory is allocated?

In C, dynamic memory is allocated from the heap using some standard library functions. The two key dynamic memory functions are malloc() and free(). The malloc() function takes a single parameter, which is the size of the requested memory area in bytes. It returns a pointer to the allocated memory.

Why do we need dynamic memory allocation?

Dynamic allocation is required when you don't know the worst case requirements for memory. Then, it is impossible to statically allocate the necessary memory, because you don't know how much you will need. Even if you know the worst case requirements, it may still be desirable to use dynamic memory allocation.

What is the use of dynamic memory allocation in C?

It is a dynamic memory allocation function which is used to allocate the memory to complex data structures such as arrays and structures. Malloc() function is used to allocate a single block of memory space while the calloc() in C is used to allocate multiple blocks of memory space.


Video Answer


2 Answers

Another disadvantage of #2 is that the caller doesn't have control over how things are allocated. This can be worked around by providing an API for the client to register his own allocation/deallocation functions (like SDL does), but even that may not be sufficiently fine-grained.

The disadvantage of #1 is that it doesn't work well when output buffers are not fixed-size (e.g. strings). At best, you will then need to provide another function to obtain the length of the buffer first so that the caller can allocate it. At worst, it is simply impossible to do so efficiently (i.e. computing length on a separate path is overly expensive over computing-and-copying in one go).

The advantage of #2 is that it allows you to expose your datatype strictly as an opaque pointer (i.e. declare the struct but don't define it, and use pointers consistently). Then you can change the definition of the struct as you see fit in future versions of your library, while clients remain compatible on binary level. With #1, you have to do it by requiring the client to specify the version inside the struct in some way (e.g. all those cbSize fields in Win32 API), and then manually write code that can handle both older and newer versions of the struct to remain binary-compatible as your library evolves.

In general, if your structs are transparent data which will not change with future minor revision of the library, I'd go with #1. If it is a more or less complicated data object and you want full encapsulation to fool-proof it for future development, go with #2.

like image 171
Pavel Minaev Avatar answered Nov 07 '22 14:11

Pavel Minaev


Method number 2 every time.

Why? because with method number 1 you have to leak implementation details to the caller. The caller has to know at least how big the struct is. You can't change the internal implementation of the object without recompiling any code that uses it.

like image 24
JeremyP Avatar answered Nov 07 '22 14:11

JeremyP