Written by Meowing Cat on 6/5/2024, 3:22:00 AM
Learn how to implement reference counting in C to manage memory and avoid memory leaks and save your sanity.
Hewwooo, Cat is back with another tutorial! š± Today, we will learn how to implement reference-counting in C and writing complicated apps/services in C just like with scripting comfortably.
![]() | C is my favorite language, it's fast, low-level, and gives you full control over your code and makes you feel like a real cat-hacker. š |
But, managing memory in C can be a real pain in the tail. šæ Because, imagine you are writing a game server or an API service for something in C... a language that you can also write operating systems š
You have to manage memory, and if you don't do it right, you will have memory leaks, and your app will crash, and you will be sad. šæ
But, don't worry, Cat is here to help you! š
Today, we will learn how to implement reference counting in C to manage memory and avoid memory leaks and save your sanity. With this technique, you can write complicated apps/services in C just like with the comfort of scripting.
Also before we start, Cat stronlgy recommend you to use his cute GDBFrontend for debugging your C/C++ apps. Itās worldās cutest debugger! š¾
Also, the Valgrind my hero saves lives and helps you to find memory leaks in your C/C++ apps. You can check it out from Valgrindās official website.
Catās PokerUnicorn server is written in C and it implements an atomic reference counting mechanism for managing memory and avoiding memory leaks and race-conditions. You can read the docs, PokerUnicornās Atomic Reference Counting.
Reference counting is a memory management technique that keeps track of the number of references to an object. When the reference count reaches zero, the object is deallocated.
Here is how it works:
This way, you can manage memory without worrying about memory leaks.
Letās implement reference counting in C with a simple example.
Simply, weāll do these steps:
struct
(and type for it) kinda a base reference-counted class.š§š»āš¦± Butā¦ Catā¦ We donāt have classes in C! šæ
š Yes, I know, but we still have
struct
s, and we can use them to implement reference counting. Just imagine they are classes. š
For this tutorial, cat is gonna teach you things in a single C header file. You can copy this code and use it in your projects.
Just create a new file called ref.h
and put all the code into it. Youāll just need to include this file in your project to use reference counting.
ref_counted_t
C type!First, letās define a struct
for our reference-counted object:
typedef struct ref_counted_t ref_counted_t;
typedef void (*ref_free_f_t)(void*);
struct ref_counted {
int count;
ref_free_f_t free_f;
};
Here, we have a struct ref_counted
called ref_counted_t
with three fields:
ref_counted_t::count
- The reference count of the object.ref_counted_t::free_f
- A function pointer to deallocate the object.For the ref_counted_t::free_f
field, we are using a function pointer that takes a void*
pointer as an argument and returns void
. This function will be called to deallocate the object when the reference count reaches zero.
We declared a type ref_free_f_t
for this function pointer.
Next, letās implement functions to init, increase ref-count and decrease (or ādecrease and free (deallocate)ā) methods (yes, actually functions!).
ref_counted_init(ref_counted, free_f)
void ref_counted_init(ref_counted_t* ref_counted, ref_free_f_t free_f) {
ref_counted->count = 0;
ref_counted->free_f = free_f;
}
This function initializes the reference count to zero and sets the deallocation function.
ref_counted_use(ref_counted)
void ref_counted_use(ref_counted_t* ref_counted) {
ref_counted->count++;
}
Simple, right?
ref_counted_leave(ref_counted)
Releasing one lock is a bit more complicated. We need to check if the reference count is zero and deallocate the object if it is.
void ref_counted_leave(void** obj_vp, ref_counted_t* ref_counted) {
void* to_free = *obj_vp;
ref_counted->count--;
if (ref_counted->count == 0) {
*obj_vp = NULL;
ref_counted->free_f(to_free);
}
}
But still simpleā¦ Letās keep going!
Donāt worry cat has the full code for you! š But donāt forget to gimme sushi snacks! You can taka a look at my GitHub for the ways to donate.
ref.h
#pragma once
typedef struct ref_counted_t ref_counted_t;
typedef void (*ref_free_f_t)(void*);
struct ref_counted_t {
int count;
ref_free_f_t free_f;
};
void ref_counted_init(ref_counted_t* ref_counted, ref_free_f_t free_f) {
ref_counted->count = 0;
ref_counted->free_f = free_f;
}
void ref_counted_use(ref_counted_t* ref_counted) {
ref_counted->count++;
}
void ref_counted_leave(void** obj_vp, ref_counted_t* ref_counted) {
void* to_free = *obj_vp;
ref_counted->count--;
if (ref_counted->count == 0) {
*obj_vp = NULL;
ref_counted->free_f(to_free);
}
}
Now, letās see how to use reference counting in your code.
Our reference-counting mechanism wants you to have a free function for your object. If your object has nothing to free as sub-resources (like a string or a file descriptor),
you can just pass standard libraryās free
(from stdlib.h
) function to ref_counted_init
.
Here is an example of how to use reference counting:
An object must have these things:
..._new()
is a good practice...._free()
.cat.c
#include <stdio.h>
#include <stdlib.h>
#include "ref.h"
typedef struct cat {
ref_counted_t ref_counted;
char* name;
int age;
} cat_t;
void cat_free(void* obj) {
printf("* Freeing cat_t object...\n");
cat_t* cat = (cat_t*)obj;
free(cat->name);
free(cat);
}
cat_t* cat_new(const char* name, int age) {
printf("* Creating cat_t object...\n");
cat_t* cat = malloc(sizeof(cat_t));
ref_counted_init(&cat->ref_counted, cat_free);
cat->name = strdup(name);
cat->age = age;
return cat;
}
int main() {
cat_t* cat = cat_new("Meowing Cat", 32);
ref_counted_use(&cat->ref_counted);
printf("Cat's age: %d\n", cat->age);
printf("Reference count: %d\n\n", cat->ref_counted.count);
ref_counted_leave((void**)&cat, &cat->ref_counted);
return 0;
}
In this example, we have a struct cat
with a name
and an age
field. We have a cat_new
function to create a new cat
object and a cat_free
function to free the object.
We use ref_counted_init
to initialize the reference count and set the deallocation function to cat_free
. We use ref_counted_use
to increase the reference count and ref_counted_leave
to decrease the reference count and deallocate the object when the reference count reaches zero.
The example creates a cat
object, increases the reference count, prints the catās age and the reference count, and then decreases the reference count and deallocates the object.
The expected output is:
* Creating cat_t object...
Cat's age: 32
Reference count: 1
* Freeing cat_t object...
As you can see, the object is created, used, and then deallocated when the reference count reaches zero.
And thatās it! You have implemented reference counting in C to manage memory and avoid memory leaks. šŗ
Important! Donāt forget to compile your code with
-Wall -Wextra -Werror
flags to catch any warnings and errors.
Important! Keep in mind that when you free a dynamic allocated object (like with
malloc
), you should not be trying to use it in any case or you should never trust to look into it in your debugger! Because, when you free an object, your operating system can give that memory to another process or use it for another purpose. So, you can see very unexpected results when you try to look into it. Please check Undefined Behavior article on Wikipedia.
Noā¦ There are also atomicity for multi-threaded and complicated apps/services! But, cat will teach you that in another tutorial!
Also, cat will teach you how to write cute C macros to make this reference counting mechanism more pwogwammer-fwiendly! š¾
For now, you can use this simple reference counting mechanism in your C projects to manage memory and avoid memory leaks.
Reference counting is a powerful memory management technique that can help you manage memory in C without worrying about memory leaks. With reference counting, you can write complicated apps/services in C just like with the comfort of scripting.
I hope you enjoyed this tutorial and learned something new. If you have any questions or feedback, feel free to ask; you can reach me on GDBFrontend Discord.
Thank you for reading! Happy code pawing! š¾