From 8354b8f77fdfd907f07574b63e910aba09232ade Mon Sep 17 00:00:00 2001 From: Nicolas Dato Date: Tue, 26 Nov 2024 11:50:27 -0300 Subject: first article, safer pointers --- zola/content/articles/_index.md | 3 + zola/content/articles/safer-pointers.md | 97 +++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 zola/content/articles/_index.md create mode 100644 zola/content/articles/safer-pointers.md (limited to 'zola/content/articles') diff --git a/zola/content/articles/_index.md b/zola/content/articles/_index.md new file mode 100644 index 0000000..a4e92e5 --- /dev/null +++ b/zola/content/articles/_index.md @@ -0,0 +1,3 @@ ++++ +title = "Articles" ++++ diff --git a/zola/content/articles/safer-pointers.md b/zola/content/articles/safer-pointers.md new file mode 100644 index 0000000..bbefdad --- /dev/null +++ b/zola/content/articles/safer-pointers.md @@ -0,0 +1,97 @@ ++++ +title = "Safer Pointers" +description = "Following some ideas to make it safer to use pointers in C" +date = 2024-11-26 +authors = ["Nicolás Dato"] + +[taxonomies] +Categories=["Software Development"] +Tags=["C", "Patterns", "Pointers"] ++++ + +## 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: +1. Encapsulating the *malloc()* and *free()* functions +2. Using *calloc()* in the new alloc function, and making the structure "fail-safe" when everything is zero. +3. Making the new *free* function set the freed pointer to *NULL* +4. Every time a pointer is passed to another function, deciding who *owns* the pointer + +### Example +```C +#include +#include +#include + +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()](https://git.ffmpeg.org/gitweb/ffmpeg.git/blob/e3b355c0be85ec47ee8b3d7790ad4c4fa26827c0:/libavutil/mem.h#l378) 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. + -- cgit v1.2.3