Created: 2024-11-26
General Idea
With great power comes great resposability
Pointers are an important feature of C, but at the same time they are prone to error. To reduce errors, and make them safer to use, I propose the following 4 tips:
- Encapsulating the malloc() and free() functions
- Using calloc() in the new alloc function, and making the structure "fail-safe" when everything is zero.
- Making the new free function set the freed pointer to NULL
- Every time a pointer is passed to another function, deciding who owns the pointer
Example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct s {
int i;
char c;
char *s;
};
struct s *s_alloc(const char *msg)
{
struct s *s;
s = calloc(1, sizeof(*s));
s->s = strdup(msg);
return s;
}
void s_free(struct s **s)
{
if (s != NULL && *s != NULL) {
free((*s)->s);
free(*s);
*s = NULL;
}
}
void print_s(const struct s *s)
{
if (s != NULL) {
printf("%s\n", s->s);
}
}
int main(int argc, char **argv)
{
struct s *s;
s = s_alloc("Hello, world!");
print_s(s);
s_free(&s);
return 0;
}
Encapsulating malloc and free, and using calloc
When allocating data structures, or new types, always create a new malloc and free functions for that structure.
This way, if the structure changes you only need to update one malloc and one free function. Also, this will hide how the structure is supposed to be allocated and freed.
And using calloc (instead of malloc) will automatically set all memory to 0. This can be beneficial if the structure is safe when everything is 0, like pointers.
Setting the pointer to NULL in the new free function
The new free function should invalidate the pointer that was freed. Usually this is setting it to NULL.
This way, it is easy check if the pointer is valid or not (or was freed or not). Also, it prevents the access to a freed section of memory, which sometimes doesn't rise any error but produces unexpected behaviour or security issues.
For instance, FFmpeg use this method with the av_freep() function.
Deciding who owns the pointer
Owning the pointer means who has the resposibility of freeing it, maybe in that moment or later. Each function or program module working with pointers must know who owns them. I try to keep the owner to whoever allocs it. So if a module creates the pointer, that module owns it. Then if that pointer is passed to another module, it should still be owned by the original module if possible.
If a function doesn't take the ownership, it should receive a const pointer when possible.
If a function takes the ownership of a pointer, it should receive a pointer-to-pointer and set it to NULL. The proposed free function works that way, it must take ownership of the function (to free it) so it sets the pointer to NULL.