The Clever Use of Void Pointers in Embedded C Language

Follow usLearn Embedded Together, learn and grow together

The Clever Use of Void Pointers in Embedded C Language

In C language, <span>void*</span> is a special pointer type known as “generic pointer” or “untyped pointer”.

Unlike regular pointers, void pointers are not associated with any specific data type, which gives them unique flexibility and a wide range of applications.

Core Advantages

1. Strong Generality, Can Point to Any Data Type

The void pointer can store the address of any type of object, which is its most significant advantage:

int a = 10;
float b = 3.14;
char c = 'X';

void *ptr;

ptr = &a;  // Points to integer
ptr = &b;  // Points to float
ptr = &c;  // Points to character

This feature makes void pointers an ideal choice for handling multiple data types.

2. Universal Interface for Memory Management Functions

The memory management functions in the C standard library use void pointers as parameters and return types:

void* malloc(size_t size);
void* calloc(size_t num, size_t size);
void* realloc(void* ptr, size_t size);
void free(void* ptr);

This design allows these functions to allocate memory for any data type:

int *int_arr = (int*)malloc(10 * sizeof(int));
double *dbl_arr = (double*)malloc(20 * sizeof(double));

3. Foundation for Implementing Generic Programming

C language does not have a template mechanism, and void pointers are the main means to achieve generic functionality. For example, the qsort function in the standard library:

void qsort(void *base, size_t nmemb, size_t size,
          int (*compar)(const void *, const void *));

Through void pointers, qsort can sort arrays of any type, and users only need to provide an appropriate comparison function.

4. Efficient Memory Operation Functions

The memory operation functions provided by the standard library also use void pointers:

void *memcpy(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);

This allows these functions to efficiently operate on any type of memory block.

5. Simulation of Object-Oriented Programming

When simulating object-oriented programming in C, void pointers are very useful:

typedef struct 
{
    void *data;
    void (*print)(void *);
} Object;

void print_int(void *data) 
{
    printf("%d\n", *(int*)data);
}

void print_float(void *data) 
{
    printf("%f\n", *(float*)data);
}

6. Interface Abstraction and Information Hiding

Void pointers can be used to create opaque data types, hiding implementation details:

// Header file
typedef void* Database;

Database create_db();
void store_data(Database db, void *data, size_t size);

// Implementation file
struct RealDatabase {
    // Implementation details
};

Database create_db() {
    return (Database)malloc(sizeof(struct RealDatabase));
}

Typical Applications

1. Implementation of Generic Data Structures

typedef struct 
{
    void **items;
    size_t size;
    size_t capacity;
} GenericArray;

void init_array(GenericArray *arr, size_t initial_capacity) 
{
    arr->items = malloc(initial_capacity * sizeof(void*));
    arr->size = 0;
    arr->capacity = initial_capacity;
}

void push_back(GenericArray *arr, void *item) 
{
    if (arr->size >= arr->capacity) 
    {
        arr->capacity *= 2;
        arr->items = realloc(arr->items, arr->capacity * sizeof(void*));
    }
    arr->items[arr->size++] = item;
}

2. Thread Parameter Passing

#include <pthread.h>

struct ThreadData 
{
    int id;
    char *message;
};

void* thread_func(void *arg) 
{
    struct ThreadData *data = (struct ThreadData*)arg;
    printf("Thread %d: %s\n", data->id, data->message);
    return NULL;
}

int main() 
{
    pthread_t thread;
    struct ThreadData data = {1, "Hello from thread"};
    
    pthread_create(&thread, NULL, thread_func, (void*)&data);
    pthread_join(thread, NULL);
    
    return 0;
}

3. User Data in Callback Functions

typedef void (*Callback)(void *user_data);

void process_data(int *data, size_t size, Callback cb, void *user_data) 
{
    // Process data...
    if (cb) 
    {
        cb(user_data);
    }
}

void print_completion(void *user_data) 
{
    printf("Processing completed by %s\n", (char*)user_data);
}

int main() 
{
    int data[100];
    process_data(data, 100, print_completion, "Main thread");
    return 0;
}

Precautions

  1. Explicit Type Conversion is Required: A void pointer must be converted to a specific type before use
  2. Cannot be Dereferenced Directly: <span>*void_ptr</span> is illegal
  3. Pointer Arithmetic is Limited: Arithmetic operations cannot be performed directly on void pointers
  4. Type Safety: Overuse may reduce code safety

Conclusion

As a generic pointer mechanism in C language, void pointers provide the following key advantages:

  1. Extremely strong generality and flexibility
  2. Unified interface for memory management functions
  3. Foundation for implementing generic programming
  4. Efficient memory operation capabilities
  5. Support for simulating object-oriented programming
  6. Ability for interface abstraction and information hiding

Although void pointers sacrifice some type safety, they remain an indispensable powerful tool for C programmers in scenarios requiring the handling of multiple data types or implementing generic functionality.

Proper use of void pointers can significantly enhance code reusability and flexibility.

The Clever Use of Void Pointers in Embedded C Language

Follow 【Learn Embedded Together】 to become better together.

If you find the article good, click “Share”, “Like”, or “Recommend”!

Leave a Comment