Hi, everyone by now, we’ve played with arrays, structs, pointers, and even some dynamic memory allocation. With Exercise 18, we move into something that really showcases the power of C: function pointers.
This might sound scary at first, but once you see it in action, it feels almost natural. Function pointers let us treat functions like data we can pass them around, store them, and call them indirectly. This is the backbone of callbacks and even simulating object oriented behavior in C.
In C, a function is just a block of code in memory. So, naturally, we can have a pointer that points to this block. That’s what a function pointer is.
Syntax
If you normally write:
int callme(int a, int b);
To make it a function pointer, you’d write:
int (*callme)(int a, int b);
The (*pointer_name)
part tells the compiler: “this is a pointer to a function returning int, that takes two ints as parameters.”
And just like arrays, you can use the pointer as if it were the function itself.
Typedef Makes it easy
typedef
to simplify:typedef int (*compare_cb)(int a, int b);
Now compare_cb
is a type—like int
or char *
—and we can pass it around without repeating that ugly syntax.
Example: Sorting with Callbacks
To see function pointers in action, let’s build a flexible bubble sort. Instead of hardcoding the comparison (ascending, descending, weird logic), we’ll let the caller decide the comparison function.
This is what object oriented languages do under the hood when they let you pass custom comparators.
Code
#include <cstdio> #include <cstdlib> #include <cerrno> #include <cstring> using namespace std; /** Error handler */ void die(const char *message) { if (errno) { perror(message); } else { printf("ERROR: %s\n", message); } exit(1); } // typedef for function pointer type typedef int (*compare_cb)(int a, int b); /** Bubble sort that uses a comparison callback */ int *bubble_sort(int *numbers, int count, compare_cb cmp) { int temp = 0; int i = 0; int j = 0; int *target = (int *)malloc(count * sizeof(int)); // cast for C++ if (!target) die("Memory error."); memcpy(target, numbers, count * sizeof(int)); for (i = 0; i < count; i++) { for (j = 0; j < count - 1; j++) { if (cmp(target[j], target[j + 1]) > 0) { temp = target[j + 1]; target[j + 1] = target[j]; target[j] = temp; } } } return target; } /** Different comparison strategies */ int sorted_order(int a, int b) { return a - b; // ascending } int reverse_order(int a, int b) { return b - a; // descending } int strange_order(int a, int b) { if (a == 0 || b == 0) { return 0; } else { return a % b; // quirky comparator } } /** Test helper */ void test_sorting(int *numbers, int count, compare_cb cmp) { int i = 0; int *sorted = bubble_sort(numbers, count, cmp); if (!sorted) die("Failed to sort as requested."); for (i = 0; i < count; i++) { printf("%d ", sorted[i]); } printf("\n"); free(sorted); // Bonus: Print raw bytes of the function unsigned char *data = (unsigned char *)cmp; for (i = 0; i < 25; i++) { printf("%02x:", data[i]); } printf("\n"); } int main(int argc, char *argv[]) { if (argc < 2) die("USAGE: ex18 4 3 1 5 6"); int count = argc - 1; int i = 0; char **inputs = argv + 1; int *numbers = (int *)malloc(count * sizeof(int)); // cast for C++ if (!numbers) die("Memory error."); for (i = 0; i < count; i++) { numbers[i] = atoi(inputs[i]); } test_sorting(numbers, count, sorted_order); test_sorting(numbers, count, reverse_order); test_sorting(numbers, count, strange_order); free(numbers); return 0; }How It Works
-
typedef int (*compare_cb)(int, int);
-
Creates a shorthand type for our comparator functions.
-
-
bubble_sort
-
Implements bubble sort, but instead of deciding “< or >” itself, it calls
cmp(a, b)
. -
This makes sorting pluggable.
-
-
Comparison Functions
-
sorted_order
→ Ascending. -
reverse_order
→ Descending. -
strange_order
→ A fun, quirky modulus-based order.
-
-
test_sorting
-
Runs the sorting, prints results, and even peeks into the raw machine code of the comparator .
-
-
main
-
Reads numbers from the command line.
-
Runs sorting with three different comparison strategies.
-
Run
ERROR: USAGE: ex18 4 3 1 5 6
Takeaways
-
Function pointers allow callbacks → code becomes flexible and reusable.
-
With
typedef
, messy syntax becomes neat. -
You can swap behaviors at runtime just by changing the function pointer.
-
This is how C does what other languages call interfaces, delegates, or strategy patterns.
Extra Experiments
-
Try passing
NULL
as the comparator → watch it crash and debug why. -
Write your own sorting algorithm (like selection sort) and plug it into the same system.
-
Open the compiled binary in a hex editor and try spotting your functions.
No comments:
Post a Comment