What are Declaring Function Pointers in C- C Programming Course in Telugu

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

PREV_LEC | NEXT_LEC

Declaring Function Pointers in C

Lecture 54 of the free C programming course: declare, assign and call a function pointer in C, understand why the parentheses matter, then test yourself with a quiz and interview questions.

A function pointer in C is a variable that stores the address of a function, so the function it refers to can be chosen while the program runs. In the previous lecture you learned that every function has an address. Here you learn the syntax to keep that address in a variable, which is the heart of callbacks, driver tables and state machines in embedded C programming.

This is the final lecture of this part of the free embedded C course, so it ends with a quiz and interview questions that you can use to prepare for real C programming for embedded systems interviews.

What You Will Learn

  • The general syntax for declaring a function pointer in C
  • Why the pointer name must be wrapped in parentheses
  • How to assign a function address and call the function through the pointer
  • How typedef makes function pointer declarations readable
  • What the compiler says when the signatures do not match
  • A 10-question quiz and 10 interview questions with answers

Prerequisites

Complete cp8_53 (function addresses) first. You should also know how to declare and call a normal function, and how pointer declarations such as int *p are read. Everything is available in this free C tutorial.

Function Pointer Syntax in C

The general form is:

return_type (*pointer_name)(parameter_type1, parameter_type2, ...);

It looks like a function declaration, but the name is preceded by a star and the pair is wrapped in parentheses. Three small examples:

int   (*op)(int, int);        /* points to a function taking two ints, returning int */
void  (*callback)(void);      /* points to a function with no arguments and no result */
float (*convert)(int);        /* points to a function taking an int, returning float */

Anatomy of a function pointer declaration

int → return type of the target function
(*op) → op is a pointer
(int, int) → parameter list of the target function

Why the Parentheses Matter

In C the function-call parentheses bind tighter than the star. Take the parentheses away and the meaning flips completely:

DeclarationMeaning
float (*fp)(int);fp is a pointer to a function that takes an int and returns a float
float *fp(int);fp is a function that takes an int and returns a pointer to float
int (*f)(void);f is a pointer to a function with no parameters returning int
int *f(void);f is a function with no parameters returning pointer to int

A simple reading method: start at the name, move right until you meet a closing parenthesis, then go left, and repeat. For float (*fp)(int) that reads “fp is a pointer (*) to a function (int) returning float”.

One pointer variable, two possible targets

op → add(int, int)  (after op = add)
op → sub(int, int)  (after op = sub)

Assigning an Address and Calling Through It

After declaring a pointer, store a function address in it using the function name alone. The function must have the same return type and parameter types as the pointer expects. Then call it in either of two equivalent ways: op(6, 3) or (*op)(6, 3).

Complete Example: Choosing an Operation

Save the following as fp_basics.c.

#include <stdio.h>

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

typedef int (*binop_fn)(int, int);

int main(void)
{
    int (*op)(int, int);            /* pointer to function(int,int) -> int */

    op = add;
    printf("op = add -> op(6, 3)    = %d\n", op(6, 3));

    op = sub;
    printf("op = sub -> (*op)(6, 3) = %d\n", (*op)(6, 3));

    binop_fn same = add;            /* same idea, readable with typedef */
    printf("typedef version         = %d\n", same(10, 20));

    printf("sizeof op               = %zu bytes\n", sizeof op);
    printf("sizeof(int (*)(int,int)) == sizeof(void *) ? %s\n",
           sizeof op == sizeof(void *) ? "yes" : "no");

    return 0;
}

Build and Run

$ gcc -std=c11 -Wall -Wextra -Wpedantic -o fp_basics fp_basics.c
$ ./fp_basics
op = add -> op(6, 3)    = 9
op = sub -> (*op)(6, 3) = 3
typedef version         = 30
sizeof op               = 8 bytes
sizeof(int (*)(int,int)) == sizeof(void *) ? yes

The same variable op ran two different functions, just by changing its value. The size line shows that on a 64-bit Linux machine a function pointer is 8 bytes. On a 32-bit microcontroller it is usually 4 bytes, but the C standard does not promise that it equals the size of a data pointer, so do not rely on it.

Making Declarations Readable with typedef

The line typedef int (*binop_fn)(int, int); creates a type name. From then on binop_fn same = add; reads almost like English. Real projects use typedefs for every callback type, for example one for a UART receive handler and another for a timer expiry handler.

What Happens If the Signature Does Not Match?

The compiler compares the function type with the pointer type. Suppose you try this:

static float half(int x) { return x / 2.0f; }
int (*wrong)(int, int) = half;     /* different parameters and return type */

GCC reports:

warning: initialization of 'int (*)(int,  int)' from incompatible pointer type
'float (*)(int)' [-Wincompatible-pointer-types]

Recent GCC versions (14 and newer) turn this into an error by default, which is the right behaviour. Calling a function through a pointer of the wrong type is undefined behaviour, so never silence the warning with a cast just to make the compiler quiet.

Real-World Uses in Embedded C Programming

  • Callbacks: a timer driver accepts a void (*cb)(void) and calls it on expiry.
  • Driver operation tables: a struct of function pointers such as open, read and write that different devices fill in differently, the same pattern Linux drivers use.
  • Command dispatch: the command code received over a serial link selects the handler.
  • Runtime selection: choose a filter or checksum routine based on configuration, without a long chain of if statements.

Common Mistakes with a Function Pointer in C

MistakeResultFix
Missing parentheses around *nameDeclares a function returning a pointerWrite (*name)
op = add(2, 3);Stores the call result, not the addressWrite op = add;
Calling an uninitialised or NULL pointerCrash or hard faultInitialise and check before calling
Wrong parameter or return typesUndefined behaviourMake the types match exactly
Casting away a warningHidden stack or register corruptionFix the signature instead

Warning: a function pointer that can be overwritten by a buffer overflow is a classic route to running attacker-chosen code. Keep dispatch tables const so they stay in flash, validate any index that selects a handler, and always check for NULL before a call.

Best Practices and Performance

  • Create a typedef for every callback type, and name it after its purpose.
  • Initialise function pointers to NULL or to a safe default handler.
  • Pass a void * user-context argument along with callbacks so one handler can serve many devices.
  • An indirect call is slightly slower than a direct one and cannot always be inlined, so avoid it in tight inner loops.
  • Prefer a table of function pointers over a long switch when the set of handlers is large and regular.

Putting a Function Pointer in C to Work

To see why this matters, picture a small firmware that supports two sensor types. Instead of testing the sensor type everywhere in the code, you store one function pointer in C that refers to the correct read routine, assign it once at start-up, and call it from the main loop. Adding a third sensor later means writing one new function and one new assignment, not editing every place that reads a value. The same idea scales up to a table of pointers, which later lectures use for command handlers.

Summary and Key Takeaways

  • Declare with return_type (*name)(params); and keep the parentheses.
  • Assign using only the function name; the signature must match.
  • Call with name(args) or (*name)(args); both are equivalent.
  • typedef makes callback types readable and reusable.
  • Check for NULL, match signatures, and protect tables from overwriting.

You now know how to declare and use a function pointer in C. With the earlier lectures of this free C programming course you can build callback-driven, table-driven embedded programs. Test yourself below.

Frequently Asked Questions

What is a function pointer in C?

A variable that holds the address of a function. You can call the function through it, and change which function it refers to at run time.

How do I declare a function pointer in C?

Use return_type (*name)(parameter types). For example int (*op)(int, int) points to functions taking two ints and returning int.

Do I write op(2, 3) or (*op)(2, 3)?

Both work and mean the same thing. The shorter form is more common.

Why is typedef recommended with function pointers?

It gives the declaration a short name, which makes arrays of pointers, struct members and function parameters much easier to read.

What happens if I call a NULL function pointer?

It is undefined behaviour. On most systems it crashes the program, and on a microcontroller it usually triggers a hard fault.

Can a function pointer have a different size from a data pointer?

Yes, the C standard allows it. On common platforms they are equal, but some architectures with separate code and data spaces use different widths.

Quiz: Test Yourself

Try each question first, then open the answer.

  1. What does float (*fp)(int); declare?

    • A) A function returning a pointer to float
    • B) A pointer to a function taking an int and returning float
    • C) An array of float pointers
    • D) A pointer to a float
    Show answer

    B. The parentheses make fp a pointer, and the rest describes the function it points to.

  2. What does float *fp(int); declare?

    • A) A pointer to a function
    • B) A float variable
    • C) A function taking an int and returning a pointer to float
    • D) A syntax error
    Show answer

    C. Without parentheses around *fp, the call parentheses bind first, so fp is a function.

  3. Given int add(int, int); and int (*p)(int, int);, which statement stores the address of add in p?

    • A) p = add();
    • B) p = add;
    • C) *p = add;
    • D) p = add(int, int);
    Show answer

    B. The function name without parentheses is its address.

  4. After p = add;, which calls are valid and equivalent?

    • A) p(2, 3) only
    • B) (*p)(2, 3) only
    • C) Both p(2, 3) and (*p)(2, 3)
    • D) Neither
    Show answer

    C. Both forms call the function.

  5. Which function can be assigned to void (*cb)(void);?

    • A) int f(void)
    • B) void f(int)
    • C) void f(void)
    • D) void f(void *)
    Show answer

    C. Only the exact return type and parameter list match.

  6. On a typical 64-bit Linux system, what is sizeof a function pointer?

    • A) 2 bytes
    • B) 4 bytes
    • C) 8 bytes
    • D) The size of the function body
    Show answer

    C. It stores an address, not the code. The code size does not matter.

  7. What does typedef int (*op_t)(int, int); create?

    • A) A variable named op_t
    • B) A function named op_t
    • C) A type name for pointers to functions of that signature
    • D) A macro
    Show answer

    C. You can then write op_t handler = add;.

  8. What is the likely result of calling a function pointer that is NULL?

    • A) It calls main
    • B) Nothing happens
    • C) Undefined behaviour, usually a crash or hard fault
    • D) It returns 0
    Show answer

    C. Always check before calling.

  9. Which output does this print?

    int twice(int x) { return 2 * x; }
    int (*p)(int) = twice;
    printf("%d", p(4) + (*p)(1));
    • A) 6
    • B) 8
    • C) 10
    • D) 12
    Show answer

    C. p(4) is 8 and (*p)(1) is 2, so the sum is 10.

  10. Why do we print function addresses with uintptr_t and PRIxPTR instead of %u?

    • A) %u is slower
    • B) An address may not fit in an unsigned int on a 64-bit system
    • C) %u prints in binary
    • D) There is no difference
    Show answer

    B. A 64-bit address is larger than a 32-bit unsigned int.

Interview Questions on Function Pointers

These come up often in embedded C interviews. Practise answering them aloud before opening the answers.

  1. What is a function pointer, and where do you use it in embedded systems?

    Show answer

    It stores the address of a function. Typical uses are interrupt vector tables, driver callbacks, command dispatch tables and runtime selection of algorithms.

  2. What is the difference between int (*p)(int) and int *p(int)?

    Show answer

    The first is a pointer to a function taking int and returning int. The second declares a function p that returns a pointer to int. The parentheses change the meaning because the call operator binds tighter than the star.

  3. How do you read a complex declaration?

    Show answer

    Start at the identifier, go right until a closing parenthesis, then left, and repeat outward. Then express each step in words.

  4. Is there any difference between fn and &fn for a function?

    Show answer

    No. Both yield the address of the function, and both can be assigned to the same pointer type.

  5. How does an interrupt vector table relate to function pointers?

    Show answer

    The table is an array of handler addresses in flash. On an interrupt the hardware reads the entry for that source and jumps to it, which is effectively a call through a function pointer.

  6. Why would you use typedef with function pointers?

    Show answer

    For readability and consistency. It names the callback type once and avoids repeating a hard-to-read declaration in structs, parameters and arrays.

  7. What happens when you call a NULL or uninitialised function pointer, and how do you protect against it?

    Show answer

    Undefined behaviour, typically a crash or hard fault. Initialise pointers, check for NULL before calling, or assign a safe default handler.

  8. Can you convert a function pointer to void *?

    Show answer

    ISO C does not guarantee it, although POSIX systems support it. For portable code keep function pointers as function pointer types, and cast between function pointer types only when you will convert back before calling.

  9. How would you design a callback registration API for a driver?

    Show answer

    Define a typedef such as void (*rx_cb_t)(void *ctx, const uint8_t *data, size_t len), and a function register_rx(rx_cb_t cb, void *ctx). The driver stores both and calls cb(ctx, data, len) when data arrives. The context pointer lets one handler serve many instances.

  10. What are the security risks of function pointers, and how do you reduce them?

    Show answer

    Overwriting a pointer can redirect execution. Keep tables const in read-only memory, validate indices used to select handlers, check for NULL, avoid exposing pointers in structs next to unchecked buffers, and enable compiler hardening options.

Finish Strong with the Free C Programming Course

You have completed another part of the free embedded C course. Keep practising, revisit the quiz, and move on to the next lecture on EmbeddedPathashala.

PREV_LEC | NEXT_LEC

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *