The Interface Concept
Part of the Object Oriented Programming section of Coddy's C journey — lesson 40 of 61.
So far, our structs have mixed data and behavior together — a name alongside a function pointer, for example. But what if a struct contained only function pointers? This creates something powerful: an interface.
An interface defines what operations are available without specifying how they work. It's a contract that says "any object implementing this interface must provide these functions."
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;The ILogger struct contains nothing but a function pointer. It has no data of its own — it purely describes behavior. The "I" prefix is a common convention indicating this is an interface.
Any code that accepts an ILogger doesn't care where the log message goes. It could print to the console, write to a file, or send data over a network. The caller just knows it can call log:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // We don't know HOW it logs
}This separation is the essence of polymorphism. The process function works with any logger implementation — you can swap behaviors without changing the function's code. In the next lesson, you'll create concrete implementations that fulfill this interface contract.
Challenge
EasyLet's build a ICalculator interface — a struct that contains only function pointers, defining what operations any calculator must support without specifying how they work.
You'll create two files to organize your code:
calculator.h: Define your interface here. Create a function pointer type calledBinaryOpthat takes twointparameters and returns anint. Then define anICalculatorstruct that contains only two function pointers:operate(of typeBinaryOp) anddescribe(a function pointer that takes no parameters and returns nothing). This struct is purely an interface — it holds no data, only behavior.main.c: Include your header and implement concrete functions that can fulfill the interface:add— returns the sum of two integersmultiply— returns the product of two integersdescribe_add— printsAdditiondescribe_multiply— printsMultiplication
ICalculatorinstance, wire it up based on input, and use it to perform a calculation.
Your program will receive three inputs: two integers and an operation code (1 for addition, 2 for multiplication).
Based on the operation code, wire your ICalculator with the appropriate pair of functions. Then call describe through the interface to print the operation name, followed by calling operate to compute and print the result.
Example output when inputs are 5, 3, and 1:
Addition
8Example output when inputs are 4, 7, and 2:
Multiplication
28The key insight is that your ICalculator struct defines a contract — any code using it knows it can call describe and operate, without knowing which specific functions are wired in. Remember to use include guards in your header file.
Cheat sheet
An interface is a struct that contains only function pointers, defining what operations are available without specifying how they work.
Defining an interface:
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;The "I" prefix is a common convention indicating this is an interface. The struct contains no data — it purely describes behavior.
Using an interface:
void process(ILogger* logger, const char* msg) {
logger->log(msg); // We don't know HOW it logs
}This separation enables polymorphism — the function works with any implementation of the interface, allowing you to swap behaviors without changing the function's code.
Try it yourself
#include <stdio.h>
#include "calculator.h"
// TODO: Implement the add function
// Returns the sum of two integers
// TODO: Implement the multiply function
// Returns the product of two integers
// TODO: Implement describe_add function
// Prints "Addition"
// TODO: Implement describe_multiply function
// Prints "Multiplication"
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO: Create an ICalculator instance
// TODO: Based on op_code (1 for add, 2 for multiply),
// wire the calculator with the appropriate functions
// TODO: Call describe through the interface
// TODO: Call operate through the interface and print the result
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