πŸ‡―πŸ‡΅ ζ—₯本θͺž | πŸ‡ΊπŸ‡Έ English
Advertisement

Function Pointers in C

Pass functions as data. Essential for qsort, callbacks, and dispatch tables.

πŸ’ͺ Not getting it first time is normal
Function-pointer declaration syntax is among the hardest in C. It's fine to learn on demand, not upfront.
How to retry
  1. Master functions and pointers first
  2. Start by using typedef to name the type
  3. Just learning how to call qsort is enough for most jobs
  4. Lock in one fact: "functions have addresses too"
πŸ’‘ Tip: Read the declaration inside-out from the name: int (*fp)(int) β€” "fp is a pointer to a function taking int, returning int."

What is a function pointer?

Every function has an address in memory. A function pointer just stores that address. You can then hold functions in variables, pass them as arguments, and put them into arrays.
#include <stdio.h>

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

int main(void) {
    // fp points to "a function taking int, returning int"
    int (*fp)(int) = square;  // function name decays to its address

    printf("%d\n", fp(5));   // 25
    printf("%d\n", (*fp)(5)); // 25, older style
    return 0;
}
Rule: a function name used as an expression automatically becomes its address. fp = square and fp = &square are equivalent, as are fp(5) and (*fp)(5).

Reading the declaration / typedef

How to read it

int (*fp)(int, int);
β‘  name is fp
β‘‘ (*fp) β€” fp is a pointer
β‘’ (int, int) β€” to a function that takes two ints
β‘£ outer int β€” returning int
β†’ "pointer to a function taking two ints, returning int"

Mind the parentheses

int *fp(int);       // ❌ NOT a function pointer
                    // it's the prototype of a function returning int*

int (*fp)(int);     // βœ… function pointer

Cleaner with typedef

typedef int (*CmpFn)(const void *, const void *);

void sort(int *a, int n, CmpFn cmp) {
    /* ... */
}
Trick: "typedef moves a type name into the variable position". Once CmpFn is defined, CmpFn x; declares a function pointer.

Callbacks

A generic "do X to each element" routine where the caller supplies X:
#include <stdio.h>

void for_each(int *a, int n, void (*fn)(int)) {
    for (int i = 0; i < n; i++) fn(a[i]);
}

void print_int(int v)    { printf("%d ", v); }
void print_square(int v) { printf("%d ", v * v); }

int main(void) {
    int a[] = {1, 2, 3, 4, 5};
    for_each(a, 5, print_int);    printf("\n");
    for_each(a, 5, print_square); printf("\n");
    return 0;
}
Separating "how to iterate" from "what to do" is fundamental to event-driven systems, GUIs, async APIs, and sorting.

Sorting anything with qsort

The standard qsort takes a comparison function as a function pointer, so it can sort any type β€” ints, strings, structs.

Signature

void qsort(void *base,
           size_t nmemb,
           size_t size,
           int (*compar)(const void *, const void *));

int ascending

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

int cmp_int_asc(const void *p1, const void *p2) {
    int a = *(const int *)p1;
    int b = *(const int *)p2;
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
}

int main(void) {
    int a[] = {5, 2, 8, 1, 9, 3};
    qsort(a, 6, sizeof(int), cmp_int_asc);
    for (int i = 0; i < 6; i++) printf("%d ", a[i]);
    // 1 2 3 5 8 9
}
Gotcha: return a - b; seems to work but overflows for large or negative values. Always compare and return -1 / 0 / 1.

Struct with two-key sort

struct Student {
    char name[32];
    int score;
};

// higher score first, ties broken by name
int cmp_student(const void *p1, const void *p2) {
    const struct Student *a = p1;
    const struct Student *b = p2;
    if (a->score != b->score)
        return (b->score > a->score) ? 1 : -1;
    return strcmp(a->name, b->name);
}
qsort(students, n, sizeof(struct Student), cmp_student);

Dispatch tables

Use a function-pointer array instead of a big switch for clean, data-driven code:
#include <stdio.h>

double op_add(double a, double b) { return a + b; }
double op_sub(double a, double b) { return a - b; }
double op_mul(double a, double b) { return a * b; }
double op_div(double a, double b) { return b != 0 ? a / b : 0; }

struct OpEntry {
    char sym;
    double (*fn)(double, double);
};

static const struct OpEntry ops[] = {
    {'+', op_add}, {'-', op_sub},
    {'*', op_mul}, {'/', op_div},
};

double calc(char op, double a, double b) {
    for (int i = 0; i < sizeof(ops) / sizeof(ops[0]); i++) {
        if (ops[i].sym == op) return ops[i].fn(a, b);
    }
    return 0;
}

int main(void) {
    printf("%.2f\n", calc('+', 3, 4));
    printf("%.2f\n", calc('*', 6, 7));
}
Adding a new operator = adding one row to the table. No need to edit logic elsewhere.

Challenges

Challenge 1: Descending sort
Write a comparator that sorts an int array in descending order.
Challenge 2: Sort strings
Sort char *words[] = {"banana","apple","cherry"}; alphabetically. Note the comparator takes const char **.
Challenge 3: filter function
Implement int filter(int *a, int n, int *out, int (*pred)(int)). Copy elements for which pred returns non-zero. Test with an even-number predicate.
Challenge 4: Traffic-light state machine
Implement red β†’ green β†’ yellow β†’ red as a function-pointer array of state handlers.