5.1.13C Programming

Function pointers — declaration, calling, use in callbacks

2,042 words9 min readdifficulty · medium3 backlinks

WHY do we even want this?


WHAT exactly is a function pointer?

A function name decays to its address. So for int add(int,int):

  • add → the function
  • &add → its address (same value, type int(*)(int,int))
  • add → also decays to that address in most expressions

HOW to declare one (derive the syntax from scratch)

Start from a normal declaration and transform it step by step.


HOW to assign and call

int add(int a, int b) { return a + b; }
 
int (*p)(int, int);   // declare
p = add;              // assign (add decays to &add); p = &add; works too
int r1 = p(3, 4);     // call via pointer  -> 7
int r2 = (*p)(3, 4);  // explicit deref, also 7 (identical)

Callbacks — the killer use

A callback = you hand a function pointer to another function, and it calls you back.

// qsort signature (from <stdlib.h>):
void qsort(void *base, size_t n, size_t size,
           int (*cmp)(const void *, const void *));

Dual coding: picture it

Figure — Function pointers — declaration, calling, use in callbacks

Arrays of function pointers (dispatch table)


typedef to stay sane


Common mistakes (Steel-man each)


Forecast-then-Verify

Recall Predict outputs before reading the answer

Given int (*ops[2])(int,int) = {add, sub}; with add/sub as above:

  1. ops[0](5,2) → ?
  2. ops[1](5,2) → ?
  3. (*ops[1])(5,2) → ?

Answers: 1) 7 2) 3 3) 3 (explicit deref ≡ plain call).


Recall Feynman: explain to a 12-year-old

Imagine a TV remote. The remote has buttons, and each button is wired to do something (turn up volume, change channel). A function pointer is like a button whose wiring you can rewire. You can say "when I press THIS button, run THAT action." A callback is when you give the TV a button and say "press this when something happens." So you hand someone a button (a function), and they press it for you at the right time. You decide the action; they decide the timing.


Active-recall flashcards

What does a function pointer store?
The address of a function (the start of its code in memory).
Declare a pointer p to a function taking two ints and returning int.
int (*p)(int, int);
Why are the parentheses in int (*p)(int,int) required?
Without them int *p(int,int) declares a function returning int*; the parens bind * to p first.
Two equivalent ways to call function pointer p with args 3,4?
p(3,4) and (*p)(3,4).
What is a callback?
A function pointer you pass to another function so it can call your code at the right moment.
In qsort, what is the type of the comparator parameter?
int (*)(const void *, const void *).
Why use (x>y)-(x<y) instead of x-y in a comparator?
It returns the correct sign without risk of integer overflow.
How do you write the type of an array of 3 function pointers int(int,int)?
int (*ops[3])(int, int);
Give a typedef naming pointer to int(int,int) as BinOp.
typedef int (*BinOp)(int, int);
What's the danger of an uninitialized function pointer?
Calling it jumps to garbage memory → undefined behaviour / crash; initialize to NULL and check.
Does add (a function name) equal &add?
Yes — a function name decays to its address; both have type int(*)(int,int).

Connections

  • Pointers in C — data pointers vs code pointers
  • qsort and the C Standard Library — real-world callback
  • Arrays in C — arrays of function pointers as dispatch tables
  • typedef — taming complex declarations
  • void pointers — generic args in callbacks
  • Polymorphism — how OOP languages implement this idea (vtables = arrays of function pointers)

Concept Map

stores

type encodes

must match target

yields address

needs

else means

enables

invoked by

both forms equal via

allows

powers

passed to

calls back your

Function pointer

Address of code

Return type plus param types

Function name decays

Declaration ret *name params

Parentheses bind * to name

p = add

Call p x or *p x

Choose function at runtime

Callback

qsort with cmp

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, normal pointer ek data ka address rakhta hai (jaise int *p). Function pointer thoda alag hai — ye ek function ka address rakhta hai, matlab memory mein jahan us function ka code start hota hai. Kyunki function bhi memory mein hi rehta hai, hum uska address le sakte hain aur ek variable mein store karke baad mein "uss variable ke through" call kar sakte hain. Isse fayda ye hai ki hum runtime pe decide kar sakte hain ki kaunsa function chalana hai — hardcode karne ki zaroorat nahi.

Declaration ka syntax thoda tricky hai: int (*p)(int,int);. Yahan (*p) ke around parentheses zaroori hain. Agar tum likh do int *p(int,int) to ye "function jo int* return karta hai" ban jayega — bilkul galat. To rule yaad rakho: star ko name ke saath bracket mein hug karao. Call karne ke do tareeke hain: p(3,4) ya (*p)(3,4) — dono same kaam karte hain.

Sabse important use hai callback. Jaise qsort function ko tum ek comparison function pass karte ho, aur qsort jab bhi do elements compare karna chahega, tumhare function ko call karega. Tumne sirf "kaise compare karna hai" decide kiya, "kab call karna hai" wo library ne. Ek hi qsort se ascending ya descending dono ho sakta hai — bas comparator function badal do. Yahi power hai.

Real life mein function pointers ka use array of function pointers (dispatch table) mein hota hai — switch-case ki jagah ops[opcode](x,y) likh do, ekdum clean aur fast. Aur jab syntax confuse kare to typedef int (*BinOp)(int,int); likh ke type ko naam de do, fir code normal variable jaisa simple lagega. Ye concept aage chal ke OOP ke polymorphism aur vtables ka base hai.

Go deeper — visual, from zero

Test yourself — C Programming

Connections