Function Pointers in Structs
Part of the Object Oriented Programming section of Coddy's C journey — lesson 38 of 61.
Until now, we've used function pointers as standalone variables or passed them as arguments. The next step toward polymorphism is embedding a function pointer directly inside a struct — giving the struct its own behavior.
When a struct contains a function pointer, each instance can hold a different function. This means two objects of the same type can behave differently when you call their function pointer:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Function pointer as a member
} Printer;The Printer struct now has two members: data (name) and behavior (print). To use it, you assign a function to the pointer during initialization:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Output: !!! Hello !!!
return 0;
}Notice how we call the function through the struct: p.print("Hello"). This looks remarkably like calling a method on an object in other languages. The struct carries both its data and the function that operates on it.
This pattern is the foundation of polymorphism in C — different instances can have different functions assigned, producing different behaviors from the same struct type.
Challenge
EasyLet's build a Notifier system that demonstrates how structs can carry their own behavior through function pointers.
You'll create two files to organize your code:
notifier.h: Define a function pointer type calledNotifyFuncthat takes aconst char*parameter and returns nothing. Then define aNotifierstruct that contains aname(aconst char*) and anotifyfunction pointer of typeNotifyFunc.main.c: Include your header and implement two notification functions:alert_notify— prints the message in the format:[ALERT] messageinfo_notify— prints the message in the format:[INFO] message
Notifierinstance, wire up the appropriate function based on input, and call the notification through the struct's function pointer.
Your program will receive two inputs: a notification type (1 for alert, 2 for info) and a message to display.
Based on the type, assign the corresponding function to your notifier's notify member, then call it through the struct to display the message.
Example output when type is 1 and message is "System starting":
[ALERT] System startingExample output when type is 2 and message is "All systems normal":
[INFO] All systems normalRemember to use include guards in your header file, and call the function through the struct member: n.notify(message)
Cheat sheet
Function pointers can be embedded directly inside structs, giving each instance its own behavior. This is a foundation for polymorphism in C.
Defining a Struct with a Function Pointer
First, define a function pointer type, then include it as a struct member:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Function pointer as a member
} Printer;Using the Struct
Assign a function to the pointer during initialization, then call it through the struct:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Output: !!! Hello !!!
return 0;
}The syntax p.print("Hello") calls the function through the struct, similar to calling a method on an object in other languages.
Polymorphic Behavior
Different instances of the same struct type can have different functions assigned, producing different behaviors from the same struct type.
Try it yourself
#include <stdio.h>
#include "notifier.h"
// TODO: Implement alert_notify function
// Should print: [ALERT] message
// TODO: Implement info_notify function
// Should print: [INFO] message
int main() {
int type;
char message[256];
// Read input
scanf("%d", &type);
getchar(); // consume newline
fgets(message, sizeof(message), stdin);
// Remove trailing newline from message if present
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Create a Notifier instance
// TODO: Based on type (1 for alert, 2 for info),
// assign the appropriate function to the notifier's notify member
// TODO: Call the notification through the struct's function pointer
// Use: n.notify(message)
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Modular Programming Basics
Header FilesInclude GuardsSource FilesStatic FunctionsRecap: Modular Calculator4Encapsulation
Opaque Pointers ConceptDefining Opaque StructsGetters and SettersValidation in SettersRecap: Secret Box2Objects and Methods
Structs as ObjectsThe 'Self' PointerConst CorrectnessPointer vs ValueHelper MethodsRecap: Point Manager5Project: Simple Bank Account
Project SetupImplementation of Account8Polymorphism
Function Pointers in StructsSimulating MethodsThe Interface ConceptImplementing InterfacesPolymorphic IterationRecap: Greeter3Object Lifecycle
Constructor PatternDestructor PatternStack InitializationDeep CopyRecap: String Wrapper6Inheritance via Composition
Struct EmbeddingThe First Member RuleAccessing Parent MembersUpcastingRecap: Shape Hierarchy