Menu
Coddy logo textTech

Simulating Methods

Part of the Object Oriented Programming section of Coddy's C journey — lesson 39 of 61.

You've seen how to place a function pointer inside a struct. The real power comes when you assign different functions to different instances of the same struct type — this is how we simulate methods with varying behavior.

Consider a struct that represents an action with customizable logic:

typedef void (*ActionFunc)(void);

typedef struct {
    const char* name;
    ActionFunc execute;
} Action;

When creating instances, you wire up different functions to each one:

void say_hello(void) {
    printf("Hello!\n");
}

void say_goodbye(void) {
    printf("Goodbye!\n");
}

int main() {
    Action greet = { "Greet", say_hello };
    Action farewell = { "Farewell", say_goodbye };
    
    greet.execute();    // Output: Hello!
    farewell.execute(); // Output: Goodbye!
    return 0;
}

Both greet and farewell are the same type (Action), yet calling execute on each produces completely different results. The struct doesn't just hold data — it holds behavior that varies per instance.

This pattern mirrors how objects work in other languages: each object can have its own implementation of a method, even though they share the same interface.

challenge icon

Challenge

Easy

Let's build a Speaker system that demonstrates how different instances of the same struct type can exhibit completely different behaviors based on which function is wired into them.

You'll create two files to organize your code:

  • speaker.h: Define a function pointer type called SpeakFunc that takes no parameters and returns nothing. Then define a Speaker struct containing a name (a const char*) and a speak function pointer of type SpeakFunc.
  • main.c: Include your header and implement three different speaking functions:
    • bark — prints Woof!
    • meow — prints Meow!
    • chirp — prints Chirp!
    Then create two Speaker instances, each wired to a different function based on input, and call their speak methods to see polymorphic behavior in action.

Your program will receive two inputs: the first speaker type (1 for bark, 2 for meow, 3 for chirp) and the second speaker type (also 1, 2, or 3).

Create two Speaker instances — you can name them anything you like (such as "Dog", "Cat", "Bird", or simply "Speaker1", "Speaker2"). Wire each one to the appropriate function based on its input code, then call speak on each instance in order.

Example output when the first type is 1 and the second type is 2:

Woof!
Meow!

Example output when the first type is 3 and the second type is 3:

Chirp!
Chirp!

The key insight here is that both speakers are the same Speaker type, yet they produce different outputs because each has a different function assigned to its speak member. Remember to use include guards in your header file.

Cheat sheet

Different instances of the same struct type can hold different function pointers, enabling polymorphic behavior.

Define a function pointer type and a struct that uses it:

typedef void (*ActionFunc)(void);

typedef struct {
    const char* name;
    ActionFunc execute;
} Action;

Create instances with different functions assigned:

void say_hello(void) {
    printf("Hello!\n");
}

void say_goodbye(void) {
    printf("Goodbye!\n");
}

int main() {
    Action greet = { "Greet", say_hello };
    Action farewell = { "Farewell", say_goodbye };
    
    greet.execute();    // Output: Hello!
    farewell.execute(); // Output: Goodbye!
    return 0;
}

Both instances share the same type but exhibit different behavior when their function pointers are called.

Try it yourself

#include <stdio.h>
#include "speaker.h"

// TODO: Implement the bark function that prints "Woof!"

// TODO: Implement the meow function that prints "Meow!"

// TODO: Implement the chirp function that prints "Chirp!"

int main() {
    int type1, type2;
    scanf("%d", &type1);
    scanf("%d", &type2);
    
    // TODO: Create two Speaker instances
    
    // TODO: Wire the first speaker's speak function based on type1
    // (1 = bark, 2 = meow, 3 = chirp)
    
    // TODO: Wire the second speaker's speak function based on type2
    // (1 = bark, 2 = meow, 3 = chirp)
    
    // TODO: Call speak on each speaker in order
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming