Inheritance in C: solved exercise with composition and pointers

  2 minutes

If you are searching for inheritance in C, the practical approach is composition plus function pointers, since C has no native inheritance model.

This pattern lets you model simple hierarchies and runtime behavior in plain C.

Model a basic hierarchy:

  1. base type Animal with name and speak behavior,
  2. Dog and Cat types that extend Animal through composition,
  3. iterate over an Animal* array and execute polymorphic behavior.
#include <stdio.h>

typedef struct Animal Animal;
typedef void (*SpeakFn)(const Animal *);

struct Animal {
    const char *name;
    SpeakFn speak;
};

typedef struct {
    Animal base;
    int energy;
} Dog;

typedef struct {
    Animal base;
    int lives;
} Cat;

void dog_speak(const Animal *a) {
    const Dog *d = (const Dog *)a;
    printf("Dog %s: woof (energy=%d)\n", d->base.name, d->energy);
}

void cat_speak(const Animal *a) {
    const Cat *c = (const Cat *)a;
    printf("Cat %s: meow (lives=%d)\n", c->base.name, c->lives);
}

Dog dog_create(const char *name, int energy) {
    Dog d;
    d.base.name = name;
    d.base.speak = dog_speak;
    d.energy = energy;
    return d;
}

Cat cat_create(const char *name, int lives) {
    Cat c;
    c.base.name = name;
    c.base.speak = cat_speak;
    c.lives = lives;
    return c;
}

int main(void) {
    Dog d = dog_create("Toby", 80);
    Cat c = cat_create("Misu", 9);

    Animal *group[] = {(Animal *)&d, (Animal *)&c};
    int n = (int)(sizeof(group) / sizeof(group[0]));

    for (int i = 0; i < n; i++) {
        group[i]->speak(group[i]);
    }

    return 0;
}
Dog Toby: woof (energy=80)
Cat Misu: meow (lives=9)
  • Copying only the base type instead of using pointers to composed objects.
  • Forgetting to initialize the function pointer.
  • Casting between incompatible types.
  • Trying to replicate full OOP inheritance complexity in C when not needed.

This pattern is useful for:

  • callback-driven engines and libraries,
  • plugin systems with a shared interface,
  • reducing coupling across modules.

It is a practical design skill for systems-level C code.

If you want a complete path with progressive difficulty:

No. In C, inheritance-like design is simulated with composition, pointers, and function tables or callbacks.

When you need swappable runtime behavior, such as strategy patterns, callbacks, or driver abstractions.

Not fully. C does not provide all OOP abstractions, but this approach covers many practical modular-design cases.