Click the blue text
Follow us
Due to changes in the public account’s push rules, please click “View” and add “Star” to receive exciting technical shares at the first time.
Source from the internet, please delete if infringing
1. Function Pointers
Before discussing callback functions, we need to understand function pointers.
As we know, the soul of C language is pointers, and we often use integer pointers, string pointers, structure pointers, etc.
int *p1;
char *p2;
STRUCT *p3; // STRUCT is the structure we defined
However, it seems that we rarely use function pointers; we usually call functions directly.
Next, let’s understand the concept and usage of function pointers.
1. Concept
A function pointer is a pointer variable that points to a function.
Usually, when we talk about pointer variables, we refer to pointers that point to an integer, character, or array, etc., while a function pointer points to a function.
Function pointers can be used to call functions and pass parameters like regular functions.
The definition of a function pointer is as follows:
Return type of function (* Pointer variable name) (Function parameter list);
“Return type of function” indicates the return type of the function that this pointer variable can point to; “Function parameter list” indicates the parameter types of the function this pointer variable can point to.
We see that the definition of a function pointer is simply changing the “function name” in the “function declaration” to “(Pointer variable name)”. However, it is important to note that the parentheses around the pointer variable name cannot be omitted, as they change the operator precedence. If omitted, it would not be a function pointer definition but rather a function declaration, indicating a function that returns a pointer type.
So how do we determine whether a pointer variable is pointing to a variable or a function? First, check if the variable name has a “*” in front; if it does, it signifies a pointer variable. Secondly, check if the variable name has parentheses with parameter types at the end; if it does, it is a function pointer; if not, it is a pointer variable to a variable.
Lastly, it is important to note that pointers to functions do not support ++ and — operations.
Generally, for convenience, we choose
typedef Return type of function (* Pointer variable name) (Function parameter list);
For example
typedef int (*Fun1)(int); // Can also be written as int (*Fun1)(int x), but it is usually not done this way.
typedef int (*Fun2)(int, int); // Two integers as parameters, returning an integer
typedef void (*Fun3)(void); // No parameters and return value
typedef void* (*Fun4)(void*); // Both parameters and return value are void* pointers
2. How to Call Functions Using Function Pointers
Here’s an example:
int Func(int x); /* Declare a function */
int (*p) (int x); /* Define a function pointer */
p = Func; /* Assign the address of Func function to pointer variable p */
p = &Func; /* Assign the address of Func function to pointer variable p */
When assigning, the function Func does not have parentheses or parameters. Since the function name Func represents the address of the function, after the assignment, the pointer variable p points to the address of the Func() code.
Now let’s write a program; after seeing this program, you will understand how to use function pointers:
#include <stdio.h>
int Max(int, int); // Function declaration
int main(void)
{
int(*p)(int, int); // Define a function pointer
int a, b, c;
p = Max; // Assign function Max to pointer variable p, making p point to the Max function
printf("please enter a and b:");
scanf("%d%d", a, b);
c = (*p)(a, b); // Call Max function through function pointer
printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
return 0;
}
int Max(int x, int y) // Define Max function
{
int z;
if (x > y)
{
z = x;
}
else
{
z = y;
}
return z;
}
Note that because the function name itself can represent the function address (pointer), when obtaining the function pointer, you can use the function name directly or take the address of the function.
p = Max can be changed to p = &Max
c = (*p)(a, b) can be changed to c = p(a, b)
3. Function Pointer as a Parameter of a Function
Since function pointer variables are variables, they can also be used as parameters to a function.
Example:
#include <stdio.h>
#include <stdlib.h>
typedef void(*FunType)(int);
// Precede with the typedef keyword, thus defining a function pointer type named FunType, rather than a FunType variable.
// The form is similar to typedef int* PINT;
void myFun(int x);
void hisFun(int x);
void herFun(int x);
void callFun(FunType fp,int x);
int main()
{
callFun(myFun,100); // Pass function pointer constant as a callback function
callFun(hisFun,200);
callFun(herFun,300);
return 0;
}
void callFun(FunType fp,int x)
{
fp(x); // Execute the passed function through fp's pointer, note that the function pointed to by fp has one parameter
}
void myFun(int x)
{
printf("myFun: %d\n",x);
}
void hisFun(int x)
{
printf("hisFun: %d\n",x);
}
void herFun(int x)
{
printf("herFun: %d\n",x);
}
Output:

4. Function Pointer as a Return Type of a Function
With the above foundation, writing a function that returns a function pointer type should not be difficult. The following example is a function that returns a function pointer:
void (* func5(int, int, float ))(int, int)
{
...
}
Here, func5 takes (int, int, float) as parameters, and its return type is void (*)(int, int). In C language, the declaration of a variable or function is also a big topic. To learn more about declarations, you can refer to my previous article – C Expert Programming: Reading Notes (Chapters 1-3). The third chapter of this book spends an entire chapter explaining how to understand C language declarations.
5. Array of Function Pointers
Before explaining callback functions, let’s introduce the array of function pointers. Since function pointers are also pointers, we can use an array to store function pointers. Let’s look at an example of an array of function pointers:
/* Method 1 */
void (*func_array_1[5])(int, int, float);
/* Method 2 */
typedef void (*p_func_array)(int, int, float);
p_func_array func_array_2[5];
Both methods above can be used to define an array of function pointers, defining an array of function pointers with 5 elements of type void (*)(int, int, float).
6. Summary of Function Pointers
- Function pointer constant: Max; function pointer variable: p;
- Function names called like (*myFun)(10) are inconvenient and not customary. That’s why the designers of C language allowed the form myFun(10) for calling (which is much more convenient and resembles the function form in mathematics).
- Function pointer variables can also be stored in an array. The declaration method is: int (*fArray[10]) (int);
2. Callback Functions
1. What is a Callback Function
Let’s first see how Baidu Baike defines a callback function:
A callback function is a function called through a function pointer. If you pass the pointer (address) of a function as a parameter to another function, when this pointer is used to call the function it points to, we say this is a callback function. A callback function is not called directly by the implementing party of the function, but is called by another party when a specific event or condition occurs, to respond to that event or condition.
This paragraph is quite long and convoluted. Below, I will illustrate what a callback is with a picture:

Suppose we want to use a sorting function to sort an array. In the main program (Main program), we first select a library sorting function (Library function) through the library. However, there are many sorting algorithms, such as bubble sort, selection sort, quick sort, merge sort, etc. We may also need to sort specific objects, such as certain structures. The library function will choose a sorting algorithm based on our needs and then call the function that implements that algorithm to complete the sorting task. This called sorting function is the callback function (Callback function).
Combining this picture with the explanation above, we can find that to implement a callback function, the key point is to pass a pointer to the function to another function (the library function in the above image), and then this function can call the callback function through this pointer. Note that callback functions are not unique to C language; almost any language has callback functions. In C language, we implement callback functions using function pointers.
My understanding is: passing a segment of executable code like a parameter to other code, which will be called and executed at some point, is called a<span>callback</span>.
If the code is executed immediately, it is called a<span>synchronous callback</span>; if executed later, it is called an<span>asynchronous callback</span>.
<span>Callback function</span> is a function called through a function pointer. If you pass the pointer (address) of a function as a parameter to another function, when this pointer is used to call the function it points to, we say this is a callback function.
A callback function is not called directly by the implementing party of the function, but is called by another party when a specific event or condition occurs, to respond to that event or condition.
2. Why Use Callback Functions?
Because it separates the caller from the callee, the caller does not care who the callee is. It only needs to know that there exists a called function with a specific prototype and constraints.
In short, a callback function allows users to pass the pointer of the method they need to call as a parameter to a function, so that the function can flexibly use different methods when handling similar events.

int Callback() ///< Callback function
{
// TODO
return 0;
}
int main() ///< Main function
{
// TODO
Library(Callback); ///< Library function calls back via function pointer
// TODO
return 0;
}
Callbacks seem like just a function call between functions, and there is no difference from normal function calls.
But upon closer inspection, a key difference can be found between the two: in a callback, the main program passes the callback function as a parameter to the library function.
In this way, as long as we change the parameters passed into the library function, we can achieve different functionalities. Doesn’t this feel very flexible? Moreover, using callback functions becomes particularly excellent when the library function is complex or invisible.
3. How to Use Callback Functions?
int Callback_1(int a) ///< Callback function 1
{
printf("Hello, this is Callback_1: a = %d ", a);
return 0;
}
int Callback_2(int b) ///< Callback function 2
{
printf("Hello, this is Callback_2: b = %d ", b);
return 0;
}
int Callback_3(int c) ///< Callback function 3
{
printf("Hello, this is Callback_3: c = %d ", c);
return 0;
}
int Handle(int x, int (*Callback)(int)) ///< Note the function pointer definition used here
{
Callback(x);
}
int main()
{
Handle(4, Callback_1);
Handle(5, Callback_2);
Handle(6, Callback_3);
return 0;
}
As seen in the above code, the parameter in the<span>Handle()</span> function is a pointer, and in the<span>main()</span> function, when calling the<span>Handle()</span> function, the function names<span>Callback_1()/Callback_2()/Callback_3()</span> are passed to it, and the function names correspond to the function pointers. In other words, callback functions are actually a usage of function pointers.
4. Below is a simple callback function example for basic arithmetic operations:
#include <stdio.h>
#include <stdlib.h>
/****************************************
* Function pointer structure
***************************************/
typedef struct _OP {
float (*p_add)(float, float);
float (*p_sub)(float, float);
float (*p_mul)(float, float);
float (*p_div)(float, float);
} OP;
/****************************************
* Add, subtract, multiply, divide functions
***************************************/
float ADD(float a, float b)
{
return a + b;
}
float SUB(float a, float b)
{
return a - b;
}
float MUL(float a, float b)
{
return a * b;
}
float DIV(float a, float b)
{
return a / b;
}
/****************************************
* Initialize function pointers
***************************************/
void init_op(OP *op)
{
op->p_add = ADD;
op->p_sub = SUB;
op->p_mul = &MUL;
op->p_div = &DIV;
}
/****************************************
* Library function
***************************************/
float add_sub_mul_div(float a, float b, float (*op_func)(float, float))
{
return (*op_func)(a, b);
}
int main(int argc, char *argv[])
{
OP *op = (OP *)malloc(sizeof(OP));
init_op(op);
/* Directly use function pointers to call functions */
printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", (op->p_add)(1.3, 2.2), (*op->p_sub)(1.3, 2.2),
(op->p_mul)(1.3, 2.2), (*op->p_div)(1.3, 2.2));
/* Call callback functions */
printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n",
add_sub_mul_div(1.3, 2.2, ADD),
add_sub_mul_div(1.3, 2.2, SUB),
add_sub_mul_div(1.3, 2.2, MUL),
add_sub_mul_div(1.3, 2.2, DIV));
return 0;
}
5. Callback Function Example (Very Useful)
A small project for a GPRS module to connect to the internet; those who have used it may know that modules like <span>2G, 4G, NB</span> need to undergo steps like module power initialization, network registration, querying network information quality, connecting to the server, etc. The example here uses a state machine function (which calls different implementation methods based on different states) to sequentially call different functions to achieve module internet connection functionality, as follows:
/********* Working Status Handling *********/
typedef struct
{
uint8_t mStatus;
uint8_t (* Funtion)(void); // Function pointer form
} M26_WorkStatus_TypeDef; // Collection of M26 working status calling functions
/**********************************************
** >M26 Working Status Collection Function
***********************************************/
M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] =
{
{GPRS_NETWORK_CLOSE, M26_PWRKEY_Off }, // Module power off
{GPRS_NETWORK_OPEN, M26_PWRKEY_On }, // Module power on
{GPRS_NETWORK_Start, M26_Work_Init }, // Pin initialization
{GPRS_NETWORK_CONF, M26_NET_Config }, // AT command configuration
{GPRS_NETWORK_LINK_CTC, M26_LINK_CTC }, // Connect to dispatch center
{GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC }, // Wait for dispatch center reply
{GPRS_NETWORK_LINK_FEM, M26_LINK_FEM }, // Connect to front end
{GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM }, // Wait for front end reply
{GPRS_NETWORK_COMM, M26_COMM }, // Normal operation
{GPRS_NETWORK_WAIT_Sig, M26_WAIT_Sig }, // Wait for signal reply
{GPRS_NETWORK_GetSignal, M26_GetSignal }, // Get signal value
{GPRS_NETWORK_RESTART, M26_RESET }, // Module restart
}
/**********************************************
** >M26 Module State Machine, sequentially calling the 12 functions inside
***********************************************/
uint8_t M26_WorkStatus_Call(uint8_t Start)
{
uint8_t i = 0;
for(i = 0; i < 12; i++)
{
if(Start == M26_WorkStatus_Tab[i].mStatus)
{
return M26_WorkStatus_Tab[i].Funtion();
}
}
return 0;
}
So, if someone wants to do an <span>NB</span> module internet connection project, they can <span>copy</span> the framework above and just modify the specific implementation inside the callback functions or add or reduce callback functions to quickly and easily achieve module internet connection.
If you are over 18 years old and find learning 【C language】 too difficult? Want to try other programming languages? Then I recommend you learn Python. There is currently a free Python zero-based course valued at 499 yuan, limited to 10 places!
▲ Scan the QR code - Get it for free
Recommended Reading
Essential Dynamic Memory Management Knowledge in C Language
Can C Language Be Replaced?
Using C Language Thread Library | Detailed Code Example Explanation
Issues to Note When Using Coroutines in C++
Click Read the Original to learn more