Generic programming is a style of programming where algorithms are written in a way that is independent of the data types they operate on. Since C lacks built-in templates (like C++), it achieves genericity primarily through the use of the void * pointer and function pointers (callbacks).
1. The void * (Generic Pointer)
A void * is a pointer type that does not specify the type of data it points to.
- Ability: A
void *can hold the address of any data type. - Restriction: You cannot directly dereference a
void *(e.g.,*void_ptr) because the compiler doesn’t know the size or type of the data to retrieve. - Use: To access the data, you must first cast the
void *to a specific, known data pointer type (e.g.,(int *)void_ptr).
Example: Generic Print Function
// Function to print a generic value, provided the data type's size and a format specifier
void print_generic(void *data_ptr, char type) {
if (type == 'i') {
// Cast the generic pointer to an integer pointer before dereferencing
printf("Integer value: %d\n", *(int *)data_ptr);
} else if (type == 'f') {
// Cast the generic pointer to a float pointer
printf("Float value: %.2f\n", *(float *)data_ptr);
}
}
2. Standard Generic Function: qsort()
The most common example of generic programming in the C standard library (<stdlib.h>) is the qsort() function, which performs a quick sort on any array type.
Syntax: void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
| Parameter | Type | Description |
base | void * | Pointer to the beginning of the array (generic). |
nmemb | size_t | Number of elements in the array. |
size | size_t | Size of a single element in bytes (e.g., sizeof(int)). |
compar | Function Pointer | A callback function that determines the sort order. |
3. The Comparison Callback
The key to qsort‘s genericity is the compar function, which you must write. It takes two generic const void * arguments, which are pointers to two elements being compared.
Comparison Function Rules
- Arguments: Must be
(const void *a, const void *b). - Logic: You must cast
aandbto the correct data type pointer, dereference them to get the values, and return an integer based on the comparison:
| Return Value | Meaning |
| $< 0$ | a comes before b. |
| $0$ | a is equal to b. |
| $> 0$ | a comes after b. |
Example: Integer Comparison Function
// Compares two integers pointed to by a and b
int compare_integers(const void *a, const void *b) {
// 1. Cast the void pointers to int pointers
const int *ptr_a = a;
const int *ptr_b = b;
// 2. Dereference to get the actual values
int val_a = *ptr_a;
int val_b = *ptr_b;
// 3. Return the difference for ascending order (a - b)
return val_a - val_b;
}
// ... Call to qsort in main() ...
int data[] = {5, 2, 8, 1};
qsort(data, 4, sizeof(int), compare_integers);
