← Back to C Course | Chapter 12: Advanced Topics | Lesson 7 of 20

C Callback Functions

What is a Callback?

A callback is a function you hand to another function as an argument, so that receiving function can call it back at the appropriate moment during its own execution, rather than the caller controlling exactly when it runs. This inversion of control is what lets library code stay generic while still letting your specific logic run at the right point.

Example: What is a Callback?

c
#include <stdio.h>
void sayHello() {
	printf("Hello");
}
void runCallback(void (*callback)()) {
	callback();
}
int main() {
	runCallback(sayHello);
	return 0;
}

Creating a Simple Callback

To accept a callback, a function declares one of its parameters as a function pointer type, then invokes that parameter like a normal function call wherever the customizable behavior should happen. This separates the fixed control-flow logic (like iterating over a list) from the variable part (what to do with each element), which the caller supplies.

Example: Creating a Simple Callback

c
#include <stdio.h>
void greet() {
	printf("Hi there");
}
void doAction(void (*action)()) {
	action();
}
int main() {
	doAction(greet);
	return 0;
}

Callbacks with Parameters

A callback's own signature can include parameters, letting the calling function pass computed values — like the current array element or an index — directly into your custom logic each time it's invoked. This lets one generic driver function support many different callbacks that each need different pieces of context to do their job.

Example: Callbacks with Parameters

c
#include <stdio.h>
void printSquare(int x) {
	printf("%d", x * x);
}
void process(int value, void (*callback)(int)) {
	callback(value);
}
int main() {
	process(5, printSquare);
	return 0;
}

Filtering Arrays with Callbacks

Passing a callback into a generic array-processing function lets that same function filter, transform, or inspect elements differently depending only on which callback was supplied, without duplicating the loop logic for each use case. This is the same pattern behind functions like a custom filterArray(arr, n, predicateFn).

Example: Filtering Arrays with Callbacks

c
#include <stdio.h>
int isEven(int n) {
	return n % 2 == 0;
}
void filterPrint(int arr[], int size, int (*test)(int)) {
	for (int i = 0; i < size; i++) {
		if (test(arr[i])) {
			printf("%d ", arr[i]);
		}
	}
}
int main() {
	int nums[4] = {1, 2, 3, 4};
	filterPrint(nums, 4, isEven);
	return 0;
}

Benefits and Use Cases of Callbacks

Callbacks are what make library functions like qsort() genuinely general-purpose: qsort() itself has no idea how to compare your specific data, so it delegates that decision entirely to the comparison callback you pass in. This lets a single sorting implementation work correctly on integers, strings, structs, or anything else you define a comparator for.

Example: Benefits and Use Cases of Callbacks

c
#include <stdio.h>
#include <stdlib.h>
int compareInts(const void *a, const void *b) {
	return (*(int*)a - *(int*)b);
}
int main() {
	int arr[3] = {3, 1, 2};
	qsort(arr, 3, sizeof(int), compareInts);
	printf("%d %d %d", arr[0], arr[1], arr[2]);
	return 0;
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.