Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 36

Function Pointers: Assignment & Invocation

Recommended resources

Hello! Welcome back.

In our previous lesson, we focused on the syntax for declaring function pointers. You learned how to create a pointer variable with a signature that matches a specific function's return type and parameters, and why the parentheses in return_type (*pointer_name)(...) are so critical.

Today, we'll put those declarations into action. The goal of this lesson is to master the two essential operations for using function pointers: assigning the address of a function to a pointer and invoking that function through the pointer. This is where the true power of function pointers begins to emerge, allowing you to write more dynamic and flexible code.

What Is a Function's Address?

As we briefly discussed, when your C code is compiled, your functions are translated into machine instructions and stored in a read-only part of memory called the code segment or text segment. A function's "address" is simply the memory address of its first instruction—its entry point.

A function pointer is a variable specifically designed to hold this entry point address.

Function Pointers: Application Memory and Function Addresses
This illustration shows how a function (`func1`) is a block of machine code instructions starting at a specific address (e.g., 216) within the application's memory. A function pointer stores this starting address.

Assigning a Function's Address

Once you've declared a function pointer with the correct signature, you can assign the address of a compatible function to it. C provides two equivalent ways to do this.

Consider this simple function:

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

And a pointer to hold its address:

int (*op_ptr)(int, int);

Method 1: Implicit Assignment (More Common)

You can simply use the function's name on the right-hand side of an assignment. In this context, the function name "decays" into a pointer to the function.

op_ptr = add; // The name 'add' provides the function's address

Method 2: Explicit Address-Of Operator (&)

You can also use the & operator, just as you would with a regular variable, to make your intent explicit.

op_ptr = &add; // Explicitly taking the address of 'add'

Both methods achieve the same result. The implicit method is far more common in practice. The key is that C understands what you mean when you assign a function name to a compatible function pointer.

How do function pointers in C work?

This Stack Overflow answer provides a concise explanation of implicit assignment. It's a quick read that confirms why both syntaxes work.

Read the short C code block and the accompanying text in the answer by user 'ar'. Focus on the comment explaining that when a function name is on the right-hand side of an assignment, C knows you mean its address.


Invoking the Function Through the Pointer

After you've assigned a function's address to a pointer, you can call that function indirectly through the pointer. Again, C offers two equivalent syntaxes for this.

Method 1: Implicit Dereference (More Common)

You can use the function pointer variable's name as if it were the function name itself. This is the cleanest and most widely used syntax.

int result = op_ptr(5, 3); // result will be 8

Method 2: Explicit Dereference (*)

You can also explicitly dereference the pointer with the * operator to get the function, and then call it. The parentheses around (*op_ptr) are required due to operator precedence.

int result = (*op_ptr)(5, 3); // result will be 8

Both syntaxes are functionally identical. The choice between them is a matter of style, but modern C code overwhelmingly prefers the simpler, implicit syntax.

The following video provides a clear walkthrough of both assignment and invocation syntaxes.

Function Pointers in C / C++

The mycodeschool channel demonstrates these concepts very clearly. This clip will walk you through declaration, assignment, and the two different ways to invoke a function via its pointer.

Please watch from 06:45 to 10:46. Pay attention to: The assignment p = &Add; (06:45) The explicit invocation (*p)(2, 3); (07:49) The alternative, implicit syntaxes for both assignment (p = Add;) and invocation (p(2, 3);) discussed starting at 08:55.


A Complete Example

Let's put all the pieces together in a single program.

C Function Pointer Declaration, Assignment, and Invocation Example
This image provides a complete, annotated visual summary of declaring a function pointer, assigning a function's address to it, and calling the function through the pointer.

Here is the code from the image, which you can compile and run to solidify your understanding:

#include <stdio.h>

// A function we can point to
int add(int a, int b) {
    return a + b;
}

// Another compatible function
int subtract(int a, int b) {
    return a - b;
}

int main() {
    // 1. Declare a function pointer 'operation_ptr'
    int (*operation_ptr)(int, int);

    // 2. Assign the address of the 'add' function to the pointer
    operation_ptr = add;
    printf("Using the 'add' function:\n");

    // 3. Invoke the function through the pointer
    int sum = operation_ptr(10, 5);
    printf("Result: %d\n\n", sum); // Output: Result: 15

    // Now, let's make it dynamic!
    // 4. Re-assign the pointer to point to the 'subtract' function
    operation_ptr = subtract;
    printf("Using the 'subtract' function:\n");
    
    // 5. Invoke the function again through the same pointer
    int difference = operation_ptr(10, 5);
    printf("Result: %d\n", difference); // Output: Result: 5

    return 0;
}

This example demonstrates the core value of function pointers: the variable operation_ptr can be changed at runtime to alter the program's behavior without any if/else or switch statements at the call site. This is a fundamental technique for building flexible systems, such as the ADAS communication stacks or gateway logic you develop, where you might need to route data to different processing handlers based on configuration or state.

Test your understanding!

You are working on a simple calculator. You have the following two functions:

#include <stdio.h>

double multiply(double x, double y) {
    return x * y;
}

double divide(double x, double y) {
    if (y != 0) {
        return x / y;
    }
    return 0; // Simplified error handling
}

In your main function, write the necessary code to:

  1. Declare a function pointer named calc_func that can point to these functions.
  2. Assign the multiply function to calc_func.
  3. Call the function through the pointer with the values 10.0 and 5.0 and print the result.
  4. Re-assign the divide function to calc_func.
  5. Call the function through the pointer with the same values and print the result.
Show answer

Here is one possible solution for your main function:

int main() {
    // 1. Declare the function pointer
    double (*calc_func)(double, double);

    // 2. Assign the 'multiply' function
    calc_func = multiply;
    
    // 3. Call 'multiply' via the pointer and print
    double product = calc_func(10.0, 5.0);
    printf("Product: %f\n", product);

    // 4. Re-assign the 'divide' function
    calc_func = divide;

    // 5. Call 'divide' via the pointer and print
    double quotient = calc_func(10.0, 5.0);
    printf("Quotient: %f\n", quotient);

    return 0;
}

This demonstrates how the same pointer variable can be used to execute different functions that share the same signature, making your code more modular and dynamic.


Conclusion

In this lesson, you have moved from theory to practice by learning how to assign function addresses and invoke functions through pointers. This is the central mechanism that makes function pointers so useful.

Key Takeaways:

  • A function's address can be assigned to a compatible function pointer either implicitly (ptr = func;) or explicitly (ptr = &func;).
  • A function can be called through its pointer either by using the pointer name directly (ptr(args);) or by explicitly dereferencing it ((*ptr)(args);).
  • The implicit, more concise syntaxes for both assignment and invocation are the modern standard.
  • By re-assigning a function pointer at runtime, you can dynamically change which code gets executed, forming the basis for powerful programming patterns.

Preview of the Next Lesson:
As you might imagine, writing int (*operation_ptr)(int, int) repeatedly, especially when passing it as a parameter to another function, can become verbose and error-prone. In our next lesson, we will learn how to use typedef to create simple, readable aliases for complex function pointer types. This is a crucial technique for writing clean and maintainable code in large-scale projects.