Function Pointers in C: Declarations, Callbacks and Runnable Examples
Learn to read function-pointer declarations, build a callback dispatch table, guard invalid selections, and write a safe qsort comparator in C.
KnowledgeGate Team
Exam prep & CS education

int (*op)(int, int) looks like punctuation until you read the declaration around its identifier. A calculator can select add, subtract or multiply from a dispatch table, while qsort can receive a comparator through the same mechanism. The worked values 18 and 6 make every callback choice and result traceable.
Function pointers in C: the function signature becomes the pointer type
Start with an ordinary function:
int add(int a, int b) {
return a + b;
}Its type accepts two int arguments and returns int. In int (*op)(int, int) = add;, op stores a pointer to a compatible function.
Read from op outwards. *op says pointer. (*op)(int, int) says pointer to a function taking two int values. The leading int is the function's return type. By contrast, int *op(int, int) declares a function returning int *. The parentheses are load-bearing.
The function designator add converts to a pointer here, so op = add; and op = &add; select the same function. Keep function and object pointers separate. A void * is not a portable generic holder for a function pointer.
Calling through a function pointer: assign, redirect and trace exact values
A typedef gives the repeated signature a readable name:
typedef int (*BinaryOp)(int, int);
BinaryOp op = add;
int first = op(9, 4);The last line calls add(9, 4), so first = 9 + 4 = 13. Define int multiply(int a, int b) { return a * b; }, then set op = multiply;. Now int second = (*op)(9, 4); evaluates 9 * 4 = 36. op(9, 4) and (*op)(9, 4) are equivalent; the first is easier to read.
Return and parameter types must agree. A function returning double or accepting different parameters does not fit BinaryOp. Use the correct callback type or an adapter. Casting an incompatible function pointer hides the mismatch and can make the call invalid.
Callback in C: a complete calculator dispatch program
A callback is a function supplied to other code, which can call the selected behaviour later. This complete program passes each operation to apply:
#include <stddef.h>
#include <stdio.h>
typedef int (*BinaryOp)(int, int);
static int add(int a, int b) {
return a + b;
}
static int subtract(int a, int b) {
return a - b;
}
static int multiply(int a, int b) {
return a * b;
}
static int apply(int x, int y, BinaryOp op) {
return op(x, y);
}
int main(void) {
BinaryOp ops[] = {add, subtract, multiply};
const char *names[] = {"add", "subtract", "multiply"};
size_t count = sizeof ops / sizeof ops[0];
for (size_t i = 0; i < count; ++i) {
printf("%s: %d\n", names[i], apply(18, 6, ops[i]));
}
return 0;
}apply receives copies of 18, 6 and one function pointer. op(x, y) invokes the selected operation:
i = 0selectsadd, so18 + 6 = 24.i = 1selectssubtract, so18 - 6 = 12.i = 2selectsmultiply, so18 * 6 = 108.
The exact output is:
add: 24
subtract: 12
multiply: 108
Function-pointer arrays and selector functions: dispatch safely
BinaryOp ops[3] is an array of three function pointers. By contrast, BinaryOp choose(char symbol) is a function returning one pointer.
static BinaryOp choose(char symbol) {
if (symbol == '+') return add;
if (symbol == '-') return subtract;
if (symbol == '*') return multiply;
return NULL;
}For BinaryOp selected = choose('*');, check selected != NULL. Then selected(7, 5) calls multiply(7, 5) and returns 35. choose('?') returns NULL, which must not be called. Without the typedef, the declaration is int (*choose(char symbol))(int, int). Decode it once, but retain the typedef in real code.
This separates selection from execution without repeating a switch at every call site.
Only ops[0], ops[1] and ops[2] are valid, so reject menu choice 3 before indexing. Coding & DSA Courses for Placements is the broader route for programming and problem-solving practice.
qsort callback example: compare and sort five exact integers
The standard library's qsort accepts a comparator callback. Include <stdlib.h>:
#include <stdio.h>
#include <stdlib.h>
static int compare_ints(const void *left, const void *right) {
int a = *(const int *)left;
int b = *(const int *)right;
return (a > b) - (a < b);
}
int main(void) {
int values[] = {31, 7, 18, 7, 25};
size_t count = sizeof values / sizeof values[0];
qsort(values, count, sizeof values[0], compare_ints);
for (size_t i = 0; i < count; ++i) {
printf("%d%s", values[i], i + 1 == count ? "\n" : " ");
}
return 0;
}The array contains five elements, so count is 5. qsort passes two element addresses to compare_ints, and the casts recover pointers to const int. The loop prints the sorted array:
7 7 18 25 31As representative sign checks, not the library's call order, (7, 31) gives 0 - 1 = -1, (7, 7) gives 0 - 0 = 0, and (31, 7) gives 1 - 0 = 1. This expression avoids the overflow risk of returning a - b. The Time complexity and asymptotic notation: Big-O, Theta and Omega explained lesson explains how to analyse a particular sorting algorithm. The qsort interface itself specifies no fixed algorithm or complexity.

Common function-pointer errors: cause, consequence and correction
Mistake | Consequence | Correction |
|---|---|---|
Omit parentheses in | Declares a function returning | Wrap |
Assign an incompatible signature | Gives the call the wrong contract | Use a matching typedef or adapter |
Call | Causes undefined behaviour | Reject |
Index beyond | Accesses outside the array | Reject menu index |
Cast a function-type mismatch | Hides a useful diagnosis | Correct the signature |
Confuse function and object pointers | Causes non-portable misuse | Keep them distinct |
If a callback accepts a double, define a compatible typedef. A function pointer identifies code but captures no locals. Pass changing state through an explicit parameter, commonly a void *context object pointer documented by the callback contract, not a global. Share the typedef with caller and receiver; enable warnings to catch mismatches.
How function pointers in C are tested in tracing and declaration questions
Semester, coding-test and interview questions ask you to decode a declarator, choose a compatible function, predict callback output, trace handlers, find a null-call path, or supply a correctly typed comparator.
Take int inc(int x) { return x + 1; }, int twice(int x) { return 2 * x; }, and int (*steps[2])(int) = {inc, twice};. In steps[1](steps[0](5)), the inner call is inc(5) = 6; the outer call is twice(6) = 12; the final value is 12.
For declarations, int (*p)(int) is a pointer to a function taking int and returning int. int *p(int) is a function taking int and returning int *. int (*p[3])(int) is an array of three compatible function pointers.
After tracing these declarations by hand, practise pointer questions that ask for a compatible signature, a selected handler or the next output value.
Function pointers in C: the short version and next practice step
Keep five rules: match the full signature, retain the declarator parentheses, use a typedef when callbacks repeat, check nullable selections before calling, and validate dispatch indexes before access.
Before compiling, add maximum(14, 9) to the dispatch array and predict 14. Write minimum(14, 9) and predict 9. Finally, change the nested trace to steps[0](steps[1](5)): twice(5) = 10, then inc(10) = 11. For a structured next step through C concepts and coding questions, use the C Language Course: Concepts, MCQs and Coding.
Keep learning

Functions in C: Syntax, Parameters and Worked Examples
Learn how C declarations, calls, and definitions fit together. Compile two complete programs, follow their values, and trace composition and recursion.

Smart Pointers in C++: unique_ptr, shared_ptr and weak_ptr with Worked Examples
Learn who owns a C++ object, when it is destroyed, and how to choose among unique_ptr, shared_ptr, weak_ptr and a borrowed raw pointer.

Pointers in C: Addresses, Arrays and Functions with Worked Examples
Build a practical pointer model with runnable C programs for dereferencing, arrays, swapping, dynamic memory and common tracing questions.

Pointers and Arrays in C: Decay, Indexing and Worked Examples
Separate arrays from pointers with runnable traces covering indexing, one-past boundaries, function parameters, two-dimensional arrays and declaration traps.