Function Pointers I
Note: This post was originally published on AH’s Blog (WordPress) on August 6, 2013, and has been migrated here.
It is a long time since I have written C++ code — I admit it is too hard to leave this language and continue coding with C#, Java, or Python. C++ has its own magic 😄
In this post I’m refreshing my C++ memory by playing with some pointers. Specifically: Function Pointers. We don’t use them that often in day-to-day projects, but they are worth understanding when talking about pointers in depth.
We all know that a pointer is a variable that holds the address of another variable. A function pointer is the same concept, but instead of pointing to a variable, it points to a function.
Declaration
int (*func_ptr)(); // points to a function that takes no arguments and returns int
The parentheses around *func_ptr are essential. Without them:
int *func_ptr(); // this is a function declaration that returns int*
Function pointers can point to functions of any return type:
long (*func_ptr_long)(); // points to a long-returning function
double (*func_ptr_doub)(); // points to a double-returning function
The declaration must match the target function’s return type and argument list:
int (*func_ptr)(); // no arguments
int (*func_ptr1)(int); // one int argument
int (*func_ptr2)(int, int); // two int arguments
Assignment
#include <stdio.h>
int func() {}
int main() {
int (*func_ptr)() = func; // assign func to the pointer
return 0;
}
The return type and argument count must match — mismatches are compile errors:
#include <stdio.h>
int func() {}
int func2(int x) {}
int main() {
double (*func_ptr)() = func; // Error: return type mismatch
int (*func_ptr2)() = func2; // Error: argument count mismatch
return 0;
}
Calling a Function via a Pointer
Explicit dereference:
#include <stdio.h>
int func(int a) {}
int main() {
int (*func_ptr)(int) = func;
(*func_ptr)(9); // explicit dereference call
return 0;
}
Implicit dereference (reads like a normal function call):
#include <stdio.h>
int func(int a) {}
int main() {
int (*func_ptr)(int) = func;
func_ptr(9); // implicit dereference call
return 0;
}
Full example — storing and printing a return value:
#include <stdio.h>
int Sum(int a, int b) {
return a + b;
}
int main() {
int (*func_ptr)(int, int) = Sum;
int Result = func_ptr(3, 4);
printf("%d\n", Result); // output: 7
return 0;
}
Function Pointers and Overloading
Function pointers work correctly with overloaded functions — the compiler resolves which overload to bind based on the pointer’s signature:
#include <stdio.h>
int func(int a) { return a + 1; }
int func(int a, int b) { return a + b; }
int main() {
int (*func_ptr1)(int); // binds to func(int)
int (*func_ptr2)(int, int); // binds to func(int, int)
func_ptr1 = func;
func_ptr2 = func;
return 0;
}
Function Pointers in Memory
Like a variable pointer, a function pointer holds a memory address. The distinction is that variable pointers point to a data location, while function pointers point to executable code — specifically to the entry point of the function after compilation.
Practical Use Case: Parameterizing Behavior
Function pointers shine when you want to change the behavior of a function at call time without duplicating logic. Here’s an example using Bubble Sort — the comparison strategy is injected via a function pointer:
#include <stdio.h>
int Arr[] = {2, 3, 1, 6, 2, 0, 0, 1};
void BubbleSort(int Length, bool (*func_ptr)(int, int)) {
for (int i = 0; i < Length - 1; i++) {
for (int j = 0; j < Length - i - 1; j++) {
if (func_ptr(Arr[j], Arr[j + 1])) {
int Tmp = Arr[j];
Arr[j] = Arr[j + 1];
Arr[j + 1] = Tmp;
}
}
}
}
inline bool Increasing(int First, int Second) { return First > Second; }
inline bool Decreasing(int First, int Second) { return First < Second; }
int main() {
BubbleSort(8, Increasing);
for (int i = 0; i < 8; i++) printf("%d\n", Arr[i]);
puts("----");
BubbleSort(8, Decreasing);
for (int i = 0; i < 8; i++) printf("%d\n", Arr[i]);
return 0;
}
The same BubbleSort function sorts ascending or descending simply by swapping the comparator — no code duplication needed.
Note: Virtual functions and polymorphism can accomplish similar goals in an object-oriented style. In modern C++, lambda functions (C++11) offer an even more ergonomic alternative — a topic worth exploring next.
