Skip to content

Function Pointers

Definition

A Function Pointer is a specific type of variable that stores the memory address of the executable code of a function, rather than storing data.

It allows programs to store functions in variables, pass them as arguments to other functions, and select which function to execute at runtime (Polymorphism).

Syntax and Anatomy

The declaration syntax is strict. The parentheses around the pointer name are mandatory.

Formula: ReturnType (*PointerName)(ParameterTypes);

  • Correct: int (*ptr)(int, int);
    • ptr is a pointer to a function.
  • Incorrect: int *ptr(int, int);
    • ptr is a function returning an int pointer (Pointer Function).

Memory Mechanics: Name vs. Address

In C, the relationship between a function's name and its address is unique compared to standard variables.

1. Equivalence of f and &f If you have a function void f(), both f and &f yield the same result (the memory address of the function). * f: The function name is a "Function Designator." In almost all expressions, it automatically decays into a pointer to the function. * &f: This is the explicit "Address-of" operation.

Therefore, f == &f is effectively true. You can assign either to a function pointer.

2. Dereferencing Loop Because a function name always decays into a pointer, dereferencing a function pointer creates a loop. * fp is a pointer. * *fp evaluates to the function itself. * The function itself immediately decays back into a pointer so it can be called.

Consequently, all the following are valid and equivalent:

fp();           // Implicit dereference (Standard usage)
(*fp)();        // Explicit dereference
(******fp)();   // Multiple dereferences (Valid due to repeated decay)

Pointer Arithmetic (Stride)

Unlike data pointers (e.g., int *, char *), function pointers have no stride.

  • Rule: You cannot perform arithmetic (fp++, fp + 1, fp - fp2) on function pointers.
  • Reason: The size of a function (sizeof(function)) is undefined in the C Standard. The compiler does not know how many bytes of machine code a function occupies, so it cannot calculate where the "next" function begins.

Usage Workflow

1. Declaration & Assignment

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

int main() {
    // Declare
    int (*op)(int, int);

    // Assign (Both valid)
    op = add;   
    op = &add;  

    return 0;
}

2. Invocation

    // Call (Both valid)
    int result = op(10, 20);      
    int result2 = (*op)(10, 20); 

Simplification with typedef

Function pointer syntax can become messy. It is best practice to use typedef to create a clear alias.

// Define a type "MathFunc" representing a function pointer
typedef int (*MathFunc)(int, int);

// Now use "MathFunc" like a normal type
MathFunc myPointer = add;

Key Use Cases

1. Callback Functions Passing a function pointer as an argument to another function. This is essential for writing generic code (e.g., qsort in the standard library).

// Generic handler that takes a logic function as a parameter
void processData(int data, void (*logicFunc)(int)) {
    logicFunc(data); // Call back the logic
}

2. Jump Tables (Strategy Pattern) Using an array of function pointers to replace massive switch-case statements.

// Array of functions
void (*actions[3])() = { openFile, saveFile, closeFile };

// Call based on index
actions[userChoice](); 

Summary of Rules

  1. Parentheses: Always use (*name) in the declaration.
  2. Assignment: ptr = func and ptr = &func are equivalent.
  3. Arithmetic: Math on function pointers is illegal (No stride).
  4. Dereference: fp() and (*fp)() are functionally identical.