It isn't possible to implement smart pointers sensibly in C because it lacks "RAII". It would be possible to implement a garbage collector by multi-threading, but one of the benefits of using C in the first place is that it doesn't have garbage collection. So essentially you are just creating a superfluous "bloat" layer here, which really doesn't add anything. Additionally you lose type safety - normal pointers in C are quite safe, but void*
are not. So overall, this code is not a good idea.
Ignoring the purpose of the program, here is a review on the pure C programming aspects:
- Whenever we use a pointer to a string literal such as
char *a = "This is some testing text";
, it should pretty much always be declared asconst char*
, because modifying a string literal invokes undefined behavior. The only time when you wouldn't useconst char*
is a scenario where you would later want to re-assign the pointer to read/write memory. is_malloc
isn't really necessary because if something is allocated withmalloc
, it either points at an address or is a null pointer. Therefore we can use the data pointer and check it vs null to tell if something was allocated. You already do that:ptr->content != NULL
.- Similarly
free(ptr)
is safe as long as the pointer either points at an address previously returned bymalloc
(& friends) or in case it is a null pointer. It is also good practice to set the pointer toNULL
after callingfree()
. - Your
struct
andunion
usage style is inconsistent. In case of theunion
you suddenly don't usetypedef
. Also, in this program as-is, you don't need struct tags but could just dotypedef struct {
. - Passing structs by value isn't really recommended since it is often slow. Your struct here is small so it's not a big deal, but the rule of thumb is to always pass structs by pointer.
Note that writing generic abstraction layers in C is hard and it's a common mistake by intermediately experienced programmers to produce a lot of such, which later on turns out to have added nothing but increased complexity, bloat and worse performance.
Personally I still have to maintain some very old code I wrote fresh out of school and it's filled with such mistakes. I can understand what I was thinking back then: generic programming and abstraction would lead to more code re-use and reduced code repetition. But it turned out that the application using the code did not need abstraction layers and type-generic programming at all, it's just a burden for the poor soul which has to maintain it (often yourself, later on).
Instead focus on the actual requirements of the specific project. If you later down the line spot an opportunity to simplify the program by adding abstraction or code re-use, then consider implementing it at that point. Excellent programmers strive to make programs simpler, bad programmers strive to make them more complex.