Question

How to Pass a Function as a Parameter in C

cfunctionpointerssyntaxparameters

Question

I want to write a C function that applies another function, passed in as a parameter, to a set of data. How can a function be passed as an argument in C?

Short Answer

By the end of this page, you will understand how C passes functions indirectly using function pointers. You will learn the syntax, how to declare and use function-pointer parameters, how calls work step by step, and where this pattern appears in real programs such as callbacks, sorting, and data processing.

Concept

In C, you do not pass a function directly in the same way you pass an int or a double. Instead, you pass a pointer to the function.

A function pointer stores the address of a function. If another function receives that address, it can call the pointed-to function later.

This matters because it lets you write generic and reusable code. Instead of hardcoding one behavior, you can let the caller decide what operation should happen.

For example, imagine you have a list of numbers and want to:

  • print each number
  • square each number
  • check whether each number is even

Rather than writing three separate loops, you can write one reusable function that loops through the data and calls a passed-in function for each item.

In C, this pattern is common in:

  • callback-based APIs
  • sorting with qsort
  • signal handlers
  • event systems
  • data transformation utilities

The key idea is:

  • function name often acts like a pointer to that function
  • function pointer type must match the function signature
  • signature means return type + parameter types

If the signatures do not match, the behavior is incorrect and may cause warnings or bugs.

Mental Model

Think of a function pointer like a remote control with one programmed button.

  • The function itself is the device that performs the action.
  • The function pointer is the remote that knows which device to activate.
  • Passing the function pointer to another function is like handing someone the remote so they can trigger that action when needed.

Another way to think about it:

  • normal data variable: holds a value
  • normal pointer: holds the address of data
  • function pointer: holds the address of executable code

So instead of saying, "do this exact task," you are saying, "when the time comes, call whatever function lives at this address."

Take Quiz

Syntax and Examples

Basic syntax

A function pointer must match the function's signature.

For a function like this:

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

A pointer to that function can be declared as:

int (*func_ptr)(int);

This means:

  • returns int
  • points to a function
  • function takes one int

You can assign the function to the pointer:

func_ptr = square;

And call it in either of these ways:

int result1 = func_ptr(5);
int result2 = (*func_ptr)(5);

Both are valid.

Passing a function as a parameter

#include <stdio.h>

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

int apply(int value, int (*operation)(int)) {
    return operation(value);
}

int main(void) {
    int result = apply(4, square);
    printf("Result: %d\n", result);
    return 0;
}

Explanation

  • square is a function that takes an int and returns an int
  • apply takes:
    • an int value
    • a function pointer named operation
  • operation(value) calls the passed-in function
  • apply(4, square) passes the address of square

Example with an array

#include <stdio.h>

void print_double(int x) {
    printf("%d\n", x * 2);
}

void for_each(int arr[], int size, void (*action)(int)) {
    for (int i = 0; i < size; i++) {
        action(arr[i]);
    }
}

int main(void) {
    int numbers[] = {1, 2, 3, 4};
    int size = sizeof(numbers) / sizeof(numbers[0]);

    for_each(numbers, size, print_double);
    return 0;
}

Here, for_each is reusable because it does not know what action will be performed. It just calls whatever function was passed in.

Step by Step Execution

Consider this example:

#include <stdio.h>

int increment(int x) {
    return x + 1;
}

int apply(int value, int (*fn)(int)) {
    return fn(value);
}

int main(void) {
    int result = apply(10, increment);
    printf("%d\n", result);
    return 0;
}

Step-by-step

  1. main starts running.
  2. apply(10, increment) is called.
  3. The value 10 is passed into apply as value.
  4. The function increment is passed into apply as fn.
  5. Inside apply, the line fn(value) runs.
  6. Since fn points to increment, this becomes increment(10).
  7. increment(10) returns 11.
  8. apply returns 11.
  9. result stores 11.
  10. printf prints 11.

Trace table

StepCodeMeaning
1apply(10, increment)Call apply with a number and a function
2value = 10First parameter received
3fn = incrementFunction pointer now refers to increment
4fn(value)Call the pointed-to function
5increment(10)Actual function execution
6return 11Result goes back to caller

Real World Use Cases

Function pointers are used whenever code needs to be flexible about what action should happen.

Common real-world uses

1. Sorting with custom rules

The C standard library function qsort accepts a comparison function pointer.

qsort(array, count, sizeof(int), compare_ints);

This lets you define how two items should be compared.

2. Callbacks in libraries

A library may let you register a function to be called later when something happens.

Examples:

  • when data arrives
  • when a timer expires
  • when a button is clicked in a GUI toolkit

3. Processing collections

You can write reusable loops that apply different actions to each item.

  • print each item
  • validate each item
  • transform each item

4. Error handling hooks

A framework may accept a custom error-reporting function.

5. Strategy selection

Different algorithms can be plugged into the same code path by passing different functions.

Take Quiz

Real Codebase Usage

In real C codebases, function pointers often appear in patterns rather than isolated examples.

Common patterns

Guarding against NULL

A function pointer parameter may be optional. Developers often check it before calling.

void run_if_present(int value, void (*fn)(int)) {
    if (fn == NULL) {
        return;
    }
    fn(value);
}

This avoids crashes from calling a null function pointer.

Callbacks for configurable behavior

Instead of hardcoding behavior, code accepts a callback.

void process_event(void (*handler)(int), int code) {
    if (handler != NULL) {
        handler(code);
    }
}

Separating iteration from action

One function handles looping; another handles what to do with each element.

void for_each(int *arr, int size, void (*action)(int)) {
    for (int i = 0; i < size; i++) {
        action(arr[i]);
    }
}

Validation and filtering logic

You can pass predicate functions that return true/false style results.

int is_even(int x) {
    return x % 2 == 0;
}

Then use them in generic utilities.

Typedefs for readability

In larger projects, developers often use typedef to simplify complex function pointer syntax.

typedef int (*operation_fn)(int);

int apply(int value, operation_fn op) {
    return op(value);
}

This makes APIs easier to read and maintain.

Common Mistakes

1. Forgetting the parentheses in the declaration

This is one of the most common errors.

Broken code:

int *fn(int);

This does not mean “pointer to a function.” It means a function returning int *.

Correct code:

int (*fn)(int);

The parentheses are important because *fn must be grouped together.

2. Using the wrong function signature

Broken code:

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

void apply(void (*fn)(int)) {
    fn(5);
}

apply(square);

Here, apply expects a function returning void, but square returns int.

Correct code:

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

void apply(int (*fn)(int)) {
    int result = fn(5);
}

3. Calling a null function pointer

Broken code:

void run(void (*fn)(void)) {
    fn();
}

If fn is NULL, this is invalid.

Safer code:

void run(void (*fn)(void)) {
    if (fn != NULL) {
        fn();
    }
}

4. Thinking you must always write &function_name

You usually pass the function name directly:

apply(5, square);

This is standard and clear. Writing &square also works in many contexts, but it is usually unnecessary.

5. Making function pointer syntax harder than needed

If the declaration is getting messy, use typedef.

typedef void (*printer_fn)(int);

This improves readability.

6. Confusing function pointers with data pointers

A pointer to int and a pointer to a function are different types and are not interchangeable.

Broken idea:

void *p = some_function;

Do not treat function pointers as ordinary data pointers unless you fully understand the platform rules and API requirements.

Comparisons

Function pointer vs normal pointer

ConceptStoresExample
Normal pointerAddress of dataint *p
Function pointerAddress of a functionint (*fn)(int)

Direct call vs function pointer call

StyleExampleWhen used
Direct function callsquare(5)When the function is known in advance
Function pointer callfn(5)When the function is chosen dynamically

Without typedef vs with typedef

StyleExampleProsCons
Without typedefint apply(int x, int (*op)(int))ExplicitCan be hard to read
With typedeftypedef int (*operation_fn)(int);Cleaner APIsAdds one extra type name

Hardcoded behavior vs callback-based behavior

ApproachExampleFlexibility
Hardcodedreturn x * x; inside the functionLow
Callback-basedreturn op(x);High

Function pointers are best when you want one function to support different behaviors without rewriting the surrounding logic.

Take Quiz

Cheat Sheet

Quick syntax

Declare a function

int square(int x);

Declare a pointer to that function

int (*fn)(int);

Assign a function to the pointer

fn = square;

Call through the pointer

fn(5);
(*fn)(5);

Function as parameter

int apply(int value, int (*op)(int)) {
    return op(value);
}

Using typedef

typedef int (*operation_fn)(int);

int apply(int value, operation_fn op) {
    return op(value);
}

Rules to remember

  • Function pointer signature must match exactly.
  • Parentheses matter: int (*fn)(int) is correct.
  • int *fn(int) means something different.
  • You can usually pass a function by writing just its name.
  • Check for NULL before calling optional callbacks.
  • fn(args) and (*fn)(args) both work.

Common patterns

  • callbacks
  • comparison functions
  • event handlers
  • reusable data-processing loops
  • configurable behavior

FAQ

How do you pass a function to another function in C?

You pass a function pointer. The receiving function declares a parameter whose type matches the passed function's signature.

What is the syntax for a function pointer in C?

A function pointer to a function returning int and taking one int looks like this:

int (*fn)(int);

Can I call a function pointer like a normal function?

Yes. If fn is a function pointer, you can write:

fn(5);

Do I need to use & when passing a function in C?

Usually no. Writing the function name is enough:

apply(5, square);

Why are parentheses required in function pointer declarations?

Without parentheses, C reads the declaration differently because of operator precedence. int (*fn)(int) means pointer to function, while int *fn(int) means function returning pointer.

Where are function pointers used in C?

They are commonly used in callbacks, qsort, event handling, dispatch tables, and reusable processing functions.

Can a function pointer be NULL?

Yes. That is why optional callback parameters are often checked before calling them.

Should I use typedef for function pointers?

In many cases, yes. It makes declarations and APIs easier to read.

Take Quiz

Mini Project

Description

Build a small C program that processes an array of integers using a passed-in function. This demonstrates how one reusable loop can perform different actions depending on which function pointer it receives.

Goal

Create a reusable array-processing function that accepts a callback and applies it to each element.

Requirements

  • Create at least two functions that each take one int parameter.
  • Write a reusable function that accepts an integer array, its size, and a function pointer.
  • Call the reusable function with different callback functions.
  • Print output so the different behaviors are easy to see.
Take Quiz

Keep learning

Related questions

View more