Skip to content

Function Pointers in C

Updated 9 min read Aimerscrew

So far, your pointers have pointed at data: numbers, arrays and other pointers. Function pointers in C point at something different: code. A function pointer stores the address of a function, which means you can keep a function in a variable, pass it to another function, or pick which one to run while the program is running. In this lesson, you'll declare and call function pointers, build a menu with an array of them, and sort data with the standard library's qsort.

🌍 Real-life example

Think of a TV remote. Each button is wired to an action: one turns the power on, one raises the volume, one switches to the next channel. The button doesn't contain the action; it just knows where the action lives. Some universal remotes even let you reprogram a button to do something else. A function pointer is that button: it stores where a function lives, and pressing it (calling it) runs that function.

What is a function pointer in C?

When your program is compiled, every function's machine code is placed somewhere in memory, so every function has an address, just like a variable. A function pointer is a variable that holds one of those addresses. Calling through the pointer runs the function it points to.

In C, a function's name on its own (without parentheses) already means "the address of this function". So add and &add give the same thing.

How do you declare a function pointer?

The declaration copies the function's signature, with (*name) in place of the function name:

int add(int a, int b);        /* a function                          */
int (*op)(int, int);          /* a pointer to any function like it   */

Read it from the name outward: op is a pointer (*op) to a function that takes (int, int) and returns int. The parentheses around *op are essential:

DeclarationMeaning
int (*op)(int, int);op is a pointer to a function returning int
int *op(int, int);op is a function that returns an int *

A function pointer can only point to functions with the same parameter types and return type. That's how the compiler checks your calls.

Assigning and calling a function pointer

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

int main(void) {
    int (*op)(int, int);

    op = add;
    printf("add: %d\n", op(6, 3));

    op = multiply;
    printf("multiply: %d\n", op(6, 3));

    op = &add;   /* the & is optional */
    printf("again: %d\n", (*op)(6, 3));
    return 0;
}
add: 9
multiply: 18
again: 9

The same line op(6, 3) ran two different functions, depending on what op held at that moment. You can call with op(6, 3) or the older style (*op)(6, 3); both do the same thing.

Passing a function to another function (callbacks)

A callback is a function you hand to another function so that it can "call you back" at the right moment. The receiving function doesn't need to know which function it got; it just calls it.

#include <stdio.h>

double half_price(double price) {
    return price / 2;
}

double ten_off(double price) {
    return price - 10;
}

void show_price(const char *label, double price, double (*rule)(double)) {
    printf("%s: $%.2f\n", label, rule(price));
}

int main(void) {
    double price = 80.0;

    show_price("Half price", price, half_price);
    show_price("Ten dollars off", price, ten_off);
    return 0;
}
Half price: $40.00
Ten dollars off: $70.00

show_price handles the printing; the discount rule is plugged in from outside. Adding a new rule later means writing one small function, with no changes to show_price.

Arrays of function pointers: a calculator menu

Because function pointers are values, you can store them in an array, just like remote buttons in a row. Instead of a long switch, the user's choice becomes an index into the array.

int (*ops[4])(int, int) = {add, subtract, multiply, divide};

Read it as: ops is an array of 4 pointers to functions taking (int, int) and returning int.

An array ops of four function pointers. ops[0] to ops[3] hold example addresses that point to the functions add, subtract, multiply and divide. ops[2](6, 3) calls multiply and returns 18
Each element of ops stores the address of a function. Calling ops[2](6, 3) follows the pointer to multiply. (Addresses are made-up examples.)
#include <stdio.h>

int add(int a, int b)      { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b)   { return a / b; }

int main(void) {
    int (*ops[4])(int, int) = {add, subtract, multiply, divide};
    const char *names[4] = {"Add", "Subtract", "Multiply", "Divide"};
    int choice;

    for (int i = 0; i < 4; i++) {
        printf("%d. %s\n", i + 1, names[i]);
    }
    printf("Choose an operation: ");
    if (scanf("%d", &choice) != 1 || choice < 1 || choice > 4) {
        printf("Invalid choice\n");
        return 1;
    }
    printf("20 and 4 gives %d\n", ops[choice - 1](20, 4));
    return 0;
}

Here's a sample run where the user typed 3:

1. Add
2. Subtract
3. Multiply
4. Divide
Choose an operation: 3
20 and 4 gives 80

Notice the check before using the choice. An index outside 0 to 3 would read a garbage "address" and calling it would crash the program, so always validate the index first.

Sorting with qsort and a comparator

The most famous use of callbacks in C is qsort from <stdlib.h>. It can sort an array of any type, because you give it a comparator: a function that compares two elements and returns a negative number, zero or a positive number.

void qsort(void *base, size_t count, size_t size,
           int (*compare)(const void *, const void *));

qsort passes your comparator two const void * pointers. A void * is a "pointer to anything", so you first convert it to the real type and then read the value.

#include <stdio.h>
#include <stdlib.h>

int ascending(const void *a, const void *b) {
    int x = *(const int *)a;
    int y = *(const int *)b;
    return (x > y) - (x < y);
}

int descending(const void *a, const void *b) {
    return ascending(b, a);
}

void print_scores(const char *label, const int scores[], size_t count) {
    printf("%s:", label);
    for (size_t i = 0; i < count; i++) {
        printf(" %d", scores[i]);
    }
    printf("\n");
}

int main(void) {
    int scores[] = {72, 95, 58, 88, 64};
    size_t count = sizeof scores / sizeof scores[0];

    qsort(scores, count, sizeof scores[0], ascending);
    print_scores("Low to high", scores, count);

    qsort(scores, count, sizeof scores[0], descending);
    print_scores("High to low", scores, count);
    return 0;
}
Low to high: 58 64 72 88 95
High to low: 95 88 72 64 58

(x > y) - (x < y) gives 1, 0 or -1. It's safer than return x - y;, which can overflow with very large or very negative numbers.

Sorting strings with qsort

For an array of strings, each element is a const char *. So the comparator receives a pointer to that pointer, a double pointer, and hands the two strings to strcmp:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int by_name(const void *a, const void *b) {
    const char *first = *(const char *const *)a;
    const char *second = *(const char *const *)b;
    return strcmp(first, second);
}

int main(void) {
    const char *cities[] = {"Tokyo", "Berlin", "Lima", "Cairo", "Oslo"};
    size_t count = sizeof cities / sizeof cities[0];

    qsort(cities, count, sizeof cities[0], by_name);

    for (size_t i = 0; i < count; i++) {
        printf("%s\n", cities[i]);
    }
    return 0;
}
Berlin
Cairo
Lima
Oslo
Tokyo

Only the pointers were rearranged; the city names themselves stayed where they were in memory.

Making function pointers readable with typedef

Function pointer syntax gets hard to read quickly. The keyword typedef gives a type a new, shorter name. You'll learn it fully in typedef in C, but here's a preview:

#include <stdio.h>

typedef int (*Operation)(int, int);

int add(int a, int b)      { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }

int calculate(Operation op, int a, int b) {
    return op(a, b);
}

int main(void) {
    Operation ops[3] = {add, subtract, multiply};
    const char *names[3] = {"Add", "Subtract", "Multiply"};

    for (int i = 0; i < 3; i++) {
        printf("%s: %d\n", names[i], calculate(ops[i], 12, 5));
    }
    return 0;
}
Add: 17
Subtract: 7
Multiply: 60

Compare Operation ops[3] with int (*ops[3])(int, int). Same meaning, much easier to read.

💡 Tip

If you've used Python, this is similar to passing a function to sorted(items, key=...). C just makes you spell out the function's type. See Python functions for a comparison.

Common mistakes with function pointers

1. Forgetting the parentheses around *name

int *op(int, int);   /* declares a function, not a pointer */

Without (*op), you've declared a function that returns int *. Assigning op = add; afterwards is a compile error.

2. Calling the function instead of passing it

show_price("Half price", price, half_price(price));   /* wrong */

This passes the result (a double), not the function. GCC reports an incompatible type for the argument. Pass the name only: half_price.

3. Mismatched signatures

Assigning a function that takes (double) to a pointer declared for (int, int) triggers an "incompatible pointer type" diagnostic. Calling through a pointer of the wrong type is undefined behavior, so fix the types instead of adding a cast.

4. Comparator that reads the wrong type

In a string comparator, casting a straight to const char * treats the pointer itself as text. The sort runs but gives nonsense or crashes. Remember: qsort gives you a pointer to each element.

Practice exercise

🏋️ Try it yourself

Write void apply_to_all(int values[], int count, int (*change)(int)) that replaces every element with change(element). Write double_it and square. Starting from {1, 2, 3, 4}, double the array and print it, then square the result and print it again.

Show solution
#include <stdio.h>

int double_it(int n) {
    return n * 2;
}

int square(int n) {
    return n * n;
}

void apply_to_all(int values[], int count, int (*change)(int)) {
    for (int i = 0; i < count; i++) {
        values[i] = change(values[i]);
    }
}

void print_values(const char *label, const int values[], int count) {
    printf("%s:", label);
    for (int i = 0; i < count; i++) {
        printf(" %d", values[i]);
    }
    printf("\n");
}

int main(void) {
    int values[4] = {1, 2, 3, 4};

    apply_to_all(values, 4, double_it);
    print_values("Doubled", values, 4);

    apply_to_all(values, 4, square);
    print_values("Squared", values, 4);
    return 0;
}
Doubled: 2 4 6 8
Squared: 4 16 36 64

What's next?

You've now seen pointers to data, to pointers and to functions. Before moving on to memory management, it's worth learning where pointers usually go wrong. Next up: common pointer mistakes in C and how to avoid them.

Key takeaways

  • A function pointer stores the address of a function, and calling it runs that function.
  • Declare one with the function's signature and (*name), for example int (*op)(int, int).
  • A function name without parentheses is its address, so op = add and op = &add are the same.
  • Callbacks let another function decide when to call your code, as qsort does with a comparator.
  • An array of function pointers turns a menu choice into an index, but always validate the index.
  • A qsort comparator receives const void * pointers to elements and returns negative, zero or positive.
  • typedef gives a function pointer type a short name, such as typedef int (*Operation)(int, int).

Frequently asked questions

What is a function pointer in C?

A function pointer is a variable that holds the address of a function. You can assign it any function with a matching signature and call that function through the pointer, for example op(2, 3).

How do you declare a function pointer in C?

Write the return type, then (*name), then the parameter types: int (*op)(int, int); declares op as a pointer to a function that takes two ints and returns an int.

Do I need & and * when using function pointers?

No. op = add and op = &add are equivalent, and so are op(1, 2) and (*op)(1, 2). Most modern code uses the shorter forms.

What is a callback function in C?

A callback is a function you pass to another function through a function pointer, so the receiver can call it later. The comparator you give to qsort is a classic callback.

Why does a qsort comparator take const void * parameters?

Because qsort works with arrays of any type, it passes generic pointers to two elements. Your comparator converts them to the real element type before comparing.