C makes it difficult to write generic code. Unlike C++, which gives you templates and virtual functions, C only has 3 mechanisms for writing generic code:
- Preprocessor macros
- Function pointers
void* pointers are far from ideal, since you lose all type safety provided by the compiler, which can result in hard-to-debug undefined behavior resulting from invalid type casts.
Preprocessor macros have well-noted drawbacks - preprocessor expansion is basically just a find/replace mechanism that happens before the compilation phase, which again can result in hard-to-debug errors. The archetypal example being something like:
#define add(x) (x+x), where
x can be incremented twice if you call
add(i++). You can write template-style generic code entirely using C-macros, but the result is really hideous and difficult to maintain.
Function pointers provide a good way to write generic code, but unfortunately they don't provide you with type generality - they merely provide the possibility of run-time polymorphism (which is why, for example, the standard library
qsort still requires a function that takes
You can also implement class hierarchies in C using structs, as is done in the GLib library which provides a generic
GObject base class. But this suffers from similar problems as using
void* pointers, since you still need to rely on potentially unsafe manual casting to up-cast and down-cast.
So yeah, C makes it hard to write code that is both generic AND safe/easy-to-maintain, which unfortunately can result in code duplication. Large C projects often use scripting languages to generate repetitive code during the build process.