The purpose of the callback function

Please add image description
Please add image description

1. Function pointer

Before talking about callback functions, we need to understand function pointers.

We all know that the soul of C language is pointers. We often use integer pointers, string pointers, structure pointers, etc.

int *p1;
char *p2;
STRUCT *p3; //STRUCT为我们定义的结构体

But it seems that we rarely use function pointers. We generally use function calls directly.

Let's take a look at the concept and usage of function pointers.

1.1 Concept

A function pointer is a pointer variable that points to a function.

Usually the pointer variable we talk about points to an integer, character or array variable, while the function pointer points to a function.

Function pointers can be used to call functions and pass parameters like normal functions.

Function pointers are defined as:

Function return value type (* pointer variable name) (function parameter list);
"Function return value type" indicates what return value type the pointer variable can point to; "Function parameter list" indicates what parameters the pointer variable can point to. List functions. In this parameter list, you only need to write the parameter type of the function.

We see that the definition of a function pointer is to change the "function name" in the "function declaration" to "(pointer variable name)". But what needs to be noted here is that the brackets at both ends of "(pointer variable name)" cannot be omitted. The brackets change the priority of the operator. If the parentheses are omitted, it is not a definition of a function pointer but a function declaration, that is, a function whose return value type is a pointer is declared.

So how to determine whether a pointer variable is a pointer variable pointing to a variable or a pointer variable pointing to a function? First, check whether there is "" in front of the variable name. If there is "", it means it is a pointer variable; secondly, check whether there are parentheses with formal parameter types after the variable name. If there is, it is a pointer variable pointing to the function, that is, a function pointer. If there is not It is a pointer variable pointing to a variable.

One last thing to note is that pointer variables to functions do not have the ++ and – operations.

Generally for ease of use, we will choose

typedef function return value type (* pointer variable name) (function parameter list);
for example

typedef int (*Fun1)(int);//声明也可写成int (*Fun1)(int x),但习惯上一般不这样。
typedef int (*Fun2)(int, int);//参数为两个整型,返回值为整型
typedef void (*Fun3)(void);//无参数和返回值
typedef void* (*Fun4)(void*);//参数和返回值都为void*指针

1.2 How to call a function using a function pointer

Let me give you an example:

int Func(int x);   /*声明一个函数*/
int (*p) (int x);  /*定义一个函数指针*/
p = Func;          /*将Func函数的首地址赋给指针变量p*/
p = &Func;          /*将Func函数的首地址赋给指针变量p*/

Function Func does not take parentheses or parameters when assigning values. Since the function name Func represents the first address of the function, after assignment, the pointer variable p points to the first address of the function Func() code.

Let's write a program. After reading this program, you will understand how to use function pointers:

#include <stdio.h>
int Max(int, int);  //函数声明
int main(void)
{
    
    
    int(*p)(int, int);  //定义一个函数指针
    int a, b, c;
    p = Max;  //把函数Max赋给指针变量p, 使p指向Max函数
    printf("please enter a and b:");
    scanf("%d%d", &a, &b);
    c = (*p)(a, b);  //通过函数指针调用Max函数
    printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
    return 0;
}
int Max(int x, int y)  //定义Max函数
{
    
    
    int z;
    if (x > y)
    {
    
    
        z = x;
    }
    else
    {
    
    
        z = y;
    }
    return z;
}

Special note is that because the function name itself can represent the function address (pointer), when obtaining the function pointer, you can directly use the function name or take the function's address.

p = Max can be changed to p = &Max
c = (*p)(a, b) can be changed to c = p(a, b)
3. The function pointer is used as a parameter of a certain function.
Since the function pointer variable is a variable, of course It can also be used as a parameter of a function.

Example:

include <stdio.h>
#include <stdlib.h>
 
typedef void(*FunType)(int);
//前加一个typedef关键字,这样就定义一个名为FunType函数指针类型,而不是一个FunType变量。
//形式同 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);//传入函数指针常量,作为回调函数
    callFun(hisFun,200);
    callFun(herFun,300);
 
    return 0;
}
 
void callFun(FunType fp,int x)
{
    
    
    fp(x);//通过fp的指针执行传递进来的函数,注意fp所指的函数有一个参数
}
 
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 function return type
With the above foundation, it should not be difficult to write a function whose return type is a function pointer. The following example is a function whose return type is 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 variables or functions is also a university subject. If you want to know more about the topic of declaration, you can refer to my previous article - C Expert Programming" Reading Notes (Chapter 1-3). Chapter 3 of this book devotes an entire chapter to explaining how to read C language declarations.

5. Function pointer array

Before starting to explain the callback function, let's finally introduce the function pointer array. Since function pointers are also pointers, we can use arrays to store function pointers. Let's look at an example of an array of function pointers:

/* 方法1 */
void (*func_array_1[5])(int, int, float);
 
/* 方法2 */
typedef void (*p_func_array)(int, int, float);
p_func_array func_array_2[5];

Both of the above methods can be used to define a function pointer array. They define a function pointer array with 5 elements and type *void (*)(int, int, float)*.

6. Summary of function pointers

Function pointer constant: Max; function pointer variable: p;

If the number calls are like (*myFun)(10), it will be inconvenient and unaccustomed to write and read. That's why the designers of the C language designed it to allow myFun(10) to be called in this form (this is much more convenient and is the same as the function form in mathematics).

Function pointer variables can also be stored in an array. Array declaration method: int (*fArray[10]) (int);

2. Callback function

2.1 What is a callback function?

Let’s first take a look at how Baidu Encyclopedia defines the callback function:

A callback function is a function called through a function pointer. If you pass a function pointer (address) as a parameter to another function, and when this pointer is used to call the function it points to, we say it is a callback function. The callback function is not called directly by the implementer of the function, but is called by another party when a specific event or condition occurs to respond to the event or condition.
This passage is relatively long and convoluted. Below I use a picture to illustrate what a callback is:

Suppose we want to use a sorting function to sort the array, then in the main program (Main program), we first select a library sorting function (Library function) through the library. But there are many sorting algorithms, including bubble sort, selection sort, quick sort, and merge sort. At the same time, we may also need to sort special objects, such as specific structures, etc. The library function will select a sorting algorithm based on our needs, and then call the function that implements the algorithm to complete the sorting work. The called sorting function is the callback function.

Combining this picture with the explanation of the callback function above, we can find that the most critical point to implement the callback function is to pass the function pointer to a function (a library function in the picture above), and then this function can The callback function is called through this pointer. Note that the callback function is not unique to the C language. There are callback functions in almost any language. In C language, we implement callback functions by using function pointers.

My understanding is: passing a piece of executable code to other codes like parameter passing, and this code will be called and executed at a certain moment, this is called a callback.

If the code is executed immediately, it is called a synchronous callback. If it is executed later, it is called an asynchronous callback.

A callback function is a function called through a function pointer. If you pass a function pointer (address) as a parameter to another function, and when this pointer is used to call the function it points to, we say it is a callback function.

The callback function is not called directly by the implementer of the function, but is called by another party when a specific event or condition occurs to respond to the event or condition.

2.2 Why use callback function?

Because you can separate the caller from the callee, the caller doesn't care who the callee is. It only needs to know that there is a called function with a specific prototype and constraints.

In short, the callback function allows the user to pass the pointer of the method to be called as a parameter to a function, so that the function can flexibly use different methods when handling similar events.

int Callback()    ///< 回调函数
{
    
    
    // TODO
    return 0;
}
int main()     ///<  主函数
{
    
    
    // TODO
    Library(Callback);  ///< 库函数通过函数指针进行回调
    // TODO
    return 0;
}

Callbacks seem to be just calls between functions, no different from ordinary function calls.

But if you look closely, you can find a key difference between the two: in the callback, the main program passes the callback function into the library function like a parameter.

In this way, as long as we change the parameters passed into the library function, we can achieve different functions. Doesn't this feel very flexible? And it is very good to use callback functions when the library functions are complex or invisible.

3 How to use callback function?

int Callback_1(int a)   ///< 回调函数1
{
    
    
    printf("Hello, this is Callback_1: a = %d ", a);
    return 0;
}
 
int Callback_2(int b)  ///< 回调函数2
{
    
    
    printf("Hello, this is Callback_2: b = %d ", b);
    return 0;
}
 
int Callback_3(int c)   ///< 回调函数3
{
    
    
    printf("Hello, this is Callback_3: c = %d ", c);
    return 0;
}
 
int Handle(int x, int (*Callback)(int)) ///< 注意这里用到的函数指针定义
{
    
    
    Callback(x);
}
 
int main()
{
    
    
    Handle(4, Callback_1);
    Handle(5, Callback_2);
    Handle(6, Callback_3);
    return 0;
}

As shown in the above code: you can see that the parameter in the Handle() function is a pointer. When calling the Handle() function in the main() function, the functions Callback_1()/Callback_2()/Callback_3() are passed to it. The function name at this time is the pointer of the corresponding function. In other words, the callback function is actually a usage of the function pointer.

4. The following is an example of a simple callback function using four arithmetic operations:

#include <stdio.h>
#include <stdlib.h>
 
/****************************************
 * 函数指针结构体
 ***************************************/
typedef struct _OP {
    
    
    float (*p_add)(float, float); 
    float (*p_sub)(float, float); 
    float (*p_mul)(float, float); 
    float (*p_div)(float, float); 
} OP; 
 
/****************************************
 * 加减乘除函数
 ***************************************/
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;
}
 
/****************************************
 * 初始化函数指针
 ***************************************/
void init_op(OP *op)
{
    
    
    op->p_add = ADD;
    op->p_sub = SUB;
    op->p_mul = &MUL;
    op->p_div = &DIV;
}
 
/****************************************
 * 库函数
 ***************************************/
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);
    
    /* 直接使用函数指针调用函数 */ 
    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));
     
    /* 调用回调函数 */ 
    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; 
}

  1. Callback function instance (very useful)

A small project for GPRS module networking. Students who have used it probably know that in order to implement wireless networking functions for 2G, 4G, NB and other modules, they need to go through steps such as module power-on initialization, network registration, network information quality query, and server connection. Here is An example is to use a state machine function (functions that call different implementation methods in sequence according to different states), and call different functions in sequence through callback functions to implement the module networking function, as follows:

/*********  工作状态处理  *********/
typedef struct
{
    
    
 uint8_t mStatus;
 uint8_t (* Funtion)(void); //函数指针的形式
} M26_WorkStatus_TypeDef;  //M26的工作状态集合调用函数
 


 
/**********************************************
** >M26工作状态集合函数
***********************************************/
M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] =
{
    
        
    {
    
    GPRS_NETWORK_CLOSE,  M26_PWRKEY_Off  }, //模块关机
    {
    
    GPRS_NETWORK_OPEN,  M26_PWRKEY_On  }, //模块开机
    {
    
    GPRS_NETWORK_Start,   M26_Work_Init  }, //管脚初始化
    {
    
    GPRS_NETWORK_CONF,  M26_NET_Config  }, /AT指令配置
    {
    
    GPRS_NETWORK_LINK_CTC,  M26_LINK_CTC  }, //连接调度中心  
    {
    
    GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC  },  //等待调度中心回复 
    {
    
    GPRS_NETWORK_LINK_FEM, M26_LINK_FEM  }, //连接前置机
    {
    
    GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM  }, //等待前置机回复
    {
    
    GPRS_NETWORK_COMM,  M26_COMM   }, //正常工作    
    {
    
    GPRS_NETWORK_WAIT_Sig,  M26_WAIT_Sig  },  //等待信号回复
    {
    
    GPRS_NETWORK_GetSignal,  M26_GetSignal  }, //获取信号值
    {
    
    GPRS_NETWORK_RESTART,  M26_RESET   }, //模块重启
}
/**********************************************
** >M26模块工作状态机,依次调用里面的12个函数   
***********************************************/
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;
}

Therefore, if someone wants to make an NB module networking project, they can copy the above framework and only need to modify the specific implementation inside the callback function, or add or reduce callback functions, and then module networking can be implemented very simply and quickly.

Guess you like

Origin blog.csdn.net/weixin_40933653/article/details/133418822