How to Embrace Object‑Oriented Design in Pure C
This article demonstrates how to simulate object‑oriented programming in C by using structs and function pointers, defines a list interface, provides a full implementation with insertion, deletion, traversal, and testing code, and explains why such techniques improve readability and maintainability.
Object‑Oriented C
Although C is traditionally procedural, its flexible type system allows developers to mimic object‑oriented concepts such as encapsulation, inheritance, and polymorphism using structures and function pointers. The following sections illustrate this approach with a concrete linked‑list example.
Basic Building Blocks
In C, a struct can represent an object’s data. For example, a point in a 2‑D plane is defined as:
typedef struct {
float x;
float y;
} Point;A node for a generic linked list can be defined as:
typedef struct node {
void *data; // data pointer
int dataLength; // data length
struct node *next; // pointer to next node
} Node;Function Pointers
Function pointers let a program store the address of a function and invoke it later, enabling callbacks and asynchronous behavior. For instance, the UNIX signal registration function is declared as:
void (*signal(int signo, void (*func)(int)))(int);Embedding a function pointer inside a struct allows the struct to carry its own operations, forming a class‑like entity.
Object‑Oriented Features in C
Key OOP features—inheritance, encapsulation, and polymorphism—can be expressed by grouping data and related functions within a struct. By assigning function pointers to struct members, each instance can invoke its own methods.
Interface Definition
The list interface declares the operations that any list implementation must provide:
#ifndef _ILIST_H
#define _ILIST_H
typedef struct node {
void *data;
void *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;
#endifThe interface makes clear which functions are available without exposing the underlying implementation.
Implementation
The concrete list is built by allocating a List object, initializing its members, and wiring the function pointers to actual implementations:
List *ListConstruction() {
List *list = (List*)malloc(sizeof(List));
Node *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;
}Key operations are implemented as follows:
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)++;
}
void drop(void *node) {
Node *t = list->_this->head;
Node *d = NULL;
for (int i = 0; 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;
}
int getSize() { return list->_this->size; }
void *get(int index) { /* omitted for brevity */ }
void print() { /* iterate and print nodes */ }
void clear() { /* free all nodes */ }Test Code
A simple main function demonstrates constructing a list, inserting several strings, printing the list, removing specific nodes, and finally clearing 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"; node.next = NULL;
list->drop(&node);
list->print();
printf("list size = %d
", list->getSize());
list->clear();
return 0;
}Conclusion
The UNIX philosophy of building simple, composable tools is well reflected in C. By treating structs as objects and attaching function pointers as methods, developers can create clean, maintainable code that behaves like object‑oriented designs while retaining C’s performance and low‑level control.
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.
