Fundamentals 11 min read

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.

Liangxu Linux
Liangxu Linux
Liangxu Linux
How to Emulate Object‑Oriented Programming in C with Structs and Function Pointers

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CfundamentalsObject-Orientedlinked listFunction Pointer
Liangxu Linux
Written by

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.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.