Master These C Language Tips to Greatly Improve Your Programming Skills

Click on the blue text
Master These C Language Tips to Greatly Improve Your Programming Skills
Follow us

Due to changes in the public account’s push rules, please click “View” and add “Star” to get exciting technical shares as soon as possible

Source from the internet, infringement will be deleted

Master These C Language Tips to Greatly Improve Your Programming Skills

1. Function Pointers

Before discussing callback functions, we need to understand function pointers.
As we all 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.
Let’s understand the concept and usage of function pointers.

1. Concept

A function pointer is a pointer variable that points to a function.
Typically, when we talk about pointer variables, we mean those that point to integer, character, or array variables, while function pointers point to functions.
Function pointers can be used like regular functions to call functions and pass parameters.
The definition of a function pointer is as follows:
Return Type (* Pointer Variable Name) (Function Parameter List);
“Return Type” indicates what type of return value the pointer variable can point to; “Function Parameter List” indicates what parameter list the pointer variable can point to. This parameter list only needs to specify the types of the function’s parameters.
We see that the definition of a function pointer is to replace the “function name” in the “function declaration” with “( Pointer Variable Name )”. However, it is important to note that the parentheses around “Pointer Variable Name” cannot be omitted, as they change the operator precedence. Omitting the parentheses would mean defining a function declaration rather than a function pointer, that is, declaring a function that returns a pointer type.
So how can we determine whether a pointer variable points to a variable or a function? First, look for the “*” before the variable name; if it exists, it indicates it’s a pointer variable. Secondly, check if there are parentheses with parameter types after the variable name; if there are, it indicates a function pointer; if not, it indicates a pointer variable.
Finally, note that pointer variables pointing to functions do not support ++ and – operations.
Usually, for convenience, we choose
typedef Return Type (* Pointer Variable Name) (Function Parameter List);
For example
typedef int (*Fun1)(int); // can also be written as int (*Fun1)(int x), but it is generally 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

Let me give you 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 function’s address, after assignment, the pointer variable p points to the address of the Func() code.
Reply "C Language" in the public account [C Language Chinese Community] to receive 500G of high-quality programming materials for free
Next, 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 the Max function to the 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 the Max function through the 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;
}
It is particularly important to note that because the function name itself can represent the function’s address (pointer), when obtaining a function pointer, you can either 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 Pointers as Parameters of a Function

Since function pointer variables are variables, they can also be used as parameters of a function.
Example:
#include <stdio.h>
#include <stdlib.h>

typedef void(*FunType)(int);
// Add the typedef keyword, this defines a function pointer type named FunType, not a FunType variable.
// The form is the same as 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:
Master These C Language Tips to Greatly Improve Your Programming Skills

4. Function Pointers as Function Return Types

With the above foundation, it should not be difficult to write 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 variables or functions is also a great topic; to learn more about declarations, you can refer to my previous article – C Expert Programming Notes (Chapters 1-3). This book spent an entire chapter discussing how to understand C language declarations.

5. Function Pointer Arrays

Before we start discussing callback functions, let’s introduce function pointer arrays. Since function pointers are also pointers, we can use arrays to store function pointers. Now let’s look at an example of a function pointer array:
/* 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 a function pointer array, defining an array of function pointers with 5 elements of type *<span>void (*)(int, int, float)</span>*.

6. Summary of Function Pointers

  1. Function pointer constant: Max; function pointer variable: p;
  2. Function name calls should be like (*myFun)(10); otherwise, both writing and reading are inconvenient and unconventional. Therefore, the designers of C language allowed myFun(10) to be called (which is much more convenient and similar to mathematical function forms).
  3. 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 that is called via 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 directly called by the implementation party of that function but is called by another party when a specific event or condition occurs, used to respond to that event or condition.
This passage is quite long and a bit convoluted. Let me illustrate what a callback is with a diagram:
Master These C Language Tips to Greatly Improve Your Programming Skills
Suppose we want to use a sorting function to sort an array; in the main program, we first select a library sorting function through a library. However, there are many sorting algorithms, such as bubble sort, selection sort, quick sort, and merge sort. We may also need to sort specific objects, such as particular structures. The library function will select one of the sorting algorithms based on our needs and call the function implementing that algorithm to complete the sorting work. The called sorting function is a callback function.
Combining this diagram with the previous explanation of callback functions, we can see that to implement a callback function, the key point is to pass the function pointer to a function (the library function in the diagram), 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 piece of executable code like a parameter to other code, and this piece of code will be called and executed at a certain moment, 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 functions</span> are functions called via function pointers. 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.
Callback functions are not directly called by the implementation party of that function but are called by another party when a specific event or condition occurs, used 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 is 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 processing similar events.
Master These C Language Tips to Greatly Improve Your Programming Skills
int Callback()    ///< Callback function
{
    // TODO
    return 0;
}
int main()     ///< Main function
{
    // TODO
    Library(Callback);  ///< Library function calls back through function pointer
    // TODO
    return 0;
}
Callbacks seem to be just function calls, and there’s not much difference from ordinary function calls.
But looking closely, we can find a key difference between the two: in a callback, the main program passes the callback function into the library function like a parameter.
This way, as long as we change the parameters passed into the library function, we can achieve different functionalities, making it very flexible. Moreover, when the library function is very complex or invisible, using callback functions becomes very advantageous.

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 code above, the parameter in the <span>Handle()</span> function is a pointer. When calling the <span>Handle()</span> function in the <span>main()</span> function, we passed the function names <span>Callback_1()/Callback_2()/Callback_3()</span> as parameters, and the function names here are the corresponding function pointers. In other words, callback functions are a way to use function pointers.

4. Here 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; 

/****************************************
 * Addition, Subtraction, Multiplication, Division 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 connecting a GPRS module to the internet, those who have used it probably know that to achieve wireless networking capabilities with modules like 2G, 4G, NB, etc., it requires steps such as powering on the module, registering the network, querying network quality, connecting to a server, etc. The example here uses a state machine function (which calls different implementation functions based on different states) to sequentially call different functions through callback functions to achieve module networking, as follows:
/*********  Work Status Processing  *********/
typedef struct
{
 uint8_t mStatus;
 uint8_t (* Function)(void); // In the form of function pointers
} M26_WorkStatus_TypeDef;  // Collection of M26 working status calling functions

/**********************************************
** > M26 Work Status Collection Functions
**********************************************/
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 the scheduling center  
    {GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC  },  // Wait for the scheduling center's reply 
    {GPRS_NETWORK_LINK_FEM, M26_LINK_FEM  }, // Connect to the front machine
    {GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM  }, // Wait for the front machine's 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, calling the 12 functions inside sequentially   
**********************************************/
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].Function();
        }
    }
    return 0;
}
Therefore, if someone wants to create an NB module networking project, they can copy the framework above, only need to modify the specific implementation inside the callback functions, or add or reduce callback functions, and they can quickly and easily achieve module networking.

If you are over 18 years old and find learning [C language] too difficult? Want to try other programming languages? I recommend you learn Python; currently, a Python zero-based course worth 499 yuan is available for free, limited to 10 spots!



▲ Scan the QR code to receive it for free

Recommended Reading

C Language Callback Functions, Essential Tips to Enhance C Skills

Those C Language Knowledge Points That Are Easy to Learn but Hard to Remember

I wish I had known these before learning C Language!

Starting Programming with C Language, These Reasons Must Be Known!

Click Read Original to learn more

Leave a Comment