How to Emulate Object‑Oriented Programming in C with Structs and Function Pointers
This article shows how C can achieve object‑oriented‑style design by using structs to model classes and function pointers as methods, providing concrete code examples such as a linked‑list implementation, interface definition, and test harness to illustrate the technique.
Object‑Oriented C
Object‑oriented languages map more naturally to human thinking, reducing complexity and improving readability. Traditional C can achieve similar qualities using structs and function pointers. This article demonstrates the approach with concrete examples.
Basic Concepts
Structs
Structs allow users to define custom data types. A struct can serve as a primitive class, e.g., a Point struct with x and y fields.
typedef struct{
float x;
float y;
} Point;Struct members can be other structs, enabling linked structures such as a node for a linked list.
typedef struct node{
void *data;
int dataLength;
struct node *next;
} Node;Function Pointers
Function pointers store the address of a function, allowing functions to be passed as arguments and invoked later, enabling patterns like callbacks.
Example: the UNIX signal registration function.
void (*signal(int signo, void (*func)(int)))(int);Function Pointers as Struct Members
Embedding function pointers in a struct creates an entity that bundles data with operations, mimicking a class.
Object‑Oriented Features
Inheritance, encapsulation, and polymorphism are core OOP features, but OOP is fundamentally a design philosophy independent of language.
In C, we can simulate these ideas by grouping data and related functions within a struct.
Language‑Level OOP Example
Define a class‑like struct Box with fields and methods.
class Box{
color;
int weight;
boolean empty;
put(something);
something get();
}Procedural code would require passing the object to global functions, which is less intuitive.
Object‑Oriented C Implementation
Interface Definition
The list interface declares operations without exposing implementation details.
#ifndef _ILIST_H
#define _ILIST_H
typedef struct node{
void *data;
struct node *next;
} Node;
typedef struct list{
struct list *_this;
Node *head;
int size;
void (*insert)(void *node);
void (*drop)(void *node);
void (*clear)();
int (*getSize)();
void* (*get)(int index);
void (*print)();
} List;
void insert(void *node);
void drop(void *node);
void clear();
int getSize();
void* get(int index);
void print();
#endif /* _ILIST_H */Implementation
Construction allocates the list and registers function pointers.
Node *node = NULL;
List *list = NULL;
void insert(void *node);
void drop(void *node);
void clear();
int getSize();
void* get(int index);
void print();
List *ListConstruction(){
list = (List*)malloc(sizeof(List));
node = (Node*)malloc(sizeof(Node));
list->head = node;
list->insert = insert;
list->drop = drop;
list->clear = clear;
list->size = 0;
list->getSize = getSize;
list->get = get;
list->print = print;
list->_this = list;
return list;
}Insert and drop functions manipulate the linked list using the stored function pointers.
// Insert a node
void insert(void *node){
Node *current = (Node*)malloc(sizeof(Node));
current->data = node;
current->next = list->_this->head->next;
list->_this->head->next = current;
(list->_this->size)++;
}
// Drop a node
void drop(void *node){
Node *t = list->_this->head;
Node *d = NULL;
int i = 0;
for(i; i < list->_this->size; i++){
d = list->_this->head->next;
if(d->data == ((Node*)node)->data){
list->_this->head->next = d->next;
free(d);
(list->_this->size)--;
break;
} else {
list->_this->head = list->_this->head->next;
}
}
list->_this->head = t;
}Test Code
The test creates a list, inserts several strings, prints the list, removes specific nodes, prints again, and finally clears the list.
int main(int argc, char** argv){
List *list = (List*)ListConstruction();
list->insert("Apple");
list->insert("Borland");
list->insert("Cisco");
list->insert("Dell");
list->insert("Electrolux");
list->insert("FireFox");
list->insert("Google");
list->print();
printf("list size = %d
", list->getSize());
Node node;
node.data = "Electrolux";
node.next = NULL;
list->drop(&node);
node.data = "Cisco";
list->drop(&node);
list->print();
printf("list size = %d
", list->getSize());
list->clear();
return 0;
}Conclusion
C inherits the Unix philosophy of simple, composable tools. Although C is procedural, its flexible constructs—structs and function pointers—allow developers to build object‑oriented‑style abstractions, giving the illusion of classes while retaining C’s power and simplicity.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
