Garbage collection requires the possibility to find objects in memory that are no longer used. One way of doing it is to have a marking algorithm that goes through all the active references. Another way is to use a reliable reference counting.
It is indeed easy to implement some reference counting features in C as your article shows. But nothing in standard C ensures that reference counting is performed reliably:
- every pointer assignment could require reference counting
- when a pointer is no longer used or needed, a reference decrement could be needed.
The C semantic is also lacking some rules that would make garbage collection possible. For example:
- Pointer arithmetics make it very difficult to use an efficient marking algorithm. Even if the compiler would help, you would have to track the use of memory ranges instead of just marking single objects.
- The use of casting could transform any place of allocated memory into a pointer at runtime, making it impossible to guarantee proper marking.
- To add to the efficiency problem: the generated code for every pointer would have to handle the case of a pointer to stack memory without GC and a pointer to heap with GC. That would be a huge overhead that no compiler builder is ready to take.
- The semantics would still need to define what cleaning activities to do when before the memory is released (also applies for the simpler case of reference counting).
Since the prerequisite cannot be fulfilled with the standard C language features, nobody attempted to create a standard GC functionality in the library.
All implementations of GC with C therefore rely on good will. For example, I remember on early MacIntosh (in the early 90's) there was a C compiler that relied on the OS memory management that used also block moving like a moving garbage collector. But whenenver you used pointer arithmetics to iterate on some allocated memory, it required you to first inform the system so that the memory does not get moved in-between (HLock/HUnlock). And it was of course totally non standard (and error prone).
Some people tried to extend the C language to add the kind of missing discipline that would be required for reference counting. One guy succeeded and brought us C++. In C++ you have reference counting smart pointers, with automatic destruction when an object is no longer used. In the standard library. But it is a very simple form of garbage collection: nothing identifies groups of objects referring to each other that are not used anywhere else. We are still far from a mark&sweep garbage collector.
FYI: a moving GC is a GC that moves non-contiguous used memory blocks, in order to increase the size of contiguous free blocks (defragmentation). This adds a level of complexity since in C pointers would have to be updated as well. In GC languages moving is not an issue because objets are accessed indriectly through an object reference that can be updated by GC.