Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

When I first learned C, the hardest thing to understand was Pointers, what are pointer variables, variable Pointers, variables that point to Pointers, Pointers to variables? A bunch of concepts, make people in the clouds in the fog, today do not discuss the problem of these concepts, from the bottom to analyze the C language why to use Pointers, Pointers exist what is the meaning of it?

To start with a simple example, write a piece of code that swaps x and y.

void main( void )
{
    u8 x = 10, y = 20;
    u8 temp;

    __asm( "sim" );                             // Disable interrupts
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             // Enable interrupts
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );

        temp = x;
        x = y;
        y = temp;

        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200); }}Copy the code

In STM8 MCU, the values of X and Y are exchanged and printed out. The printed results are as follows:

The x and y values are easily swapped using the third variable temp.

But for the sake of program aesthetics, do not want to write so much code in the main program, so decided to use a function outside to achieve this function.

void swap( u8 x, u8 y )
{
    u8 temp;
    temp = x;
    x = y;
    y = temp;
}

void main( void )
{
    u8 x = 10, y = 20;
    __asm( "sim" );                             // Disable interrupts
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             // Enable interrupts
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );
        sawp( x, y );
        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200); }}Copy the code

Define a function outside of the main function that swaps x and y. The running results of the program are as follows:

Now what happens is that the values of x and y are not swapped? So inside the swap function, you also print out the values of x and y. Modify the code as follows:

void swap( u8 x, u8 y )
{
    u8 temp;
    
    printf( "in: x = %d,y = %d\r\n", x, y );
    temp = x;
    x = y;
    y = temp;
    
    printf( "in: ---> x = %d,y = %d \r\n\r\n\r\n", x, y );
}
Copy the code

The print result is as follows:

You can see that inside the swap function, the x and y values have been swapped, but outside the function, the x and y values have not been swapped. Why is that? Single-step debugging directly observed the partial storage of X and Y in the microcontroller.

After entering the main program, first observe the memory storage of X and Y in the main function. X value is 10, namely, hexadecimal 0x0A, which is stored in the memory 0x000009, y value is 20, namely, hexadecimal 0x14, which is stored in the memory 0x00000B, which is stored. Let’s go to swap. For ease of observation, replace the x and y inside swap with m and n.

You can see that when you enter the subfunction, m and n have the same address and the same value as x and y in main. And then switch m and n.

After the swap is complete, it is found that the value of 0x000009 and the value of 0x00000B subswapped in memory. Then exit the subfunction and return to main.

And then something strange happens, what was swapped in memory? Why is that? Here has to say in C questions about local variables, local variables in the C language has no fixed location, it is managed by unified system of stack, when entering the function, the system will give these temporary assigned a local variable storage space to store its value, when the program is to leave the function, The value of the local variable is stored on the stack, and the location of the variable is released. When a program enters another function and allocates space for local variables inside that function, it is possible that the local variables in both functions use the same memory space. This change does not affect the value of the local variable in the previous function, because the local variable in the previous function is stored on the stack. When the program leaves the current function, it stores the current local variable value on the stack. After returning to the previous function, the value stored on the stack is restored to the variable, and the address of the variable is temporarily requested again, possibly with the same value as the last time. But that doesn’t mean the address will always belong to the local variable.

It’s very similar to the lockers in the supermarket, where you go to buy something, you put it in a locker, and then you take it out of the locker. Then a few hours later, I have to go to the supermarket again to buy things, and I need to store things again, but the number of the storage cabinet is the same as last time. But that doesn’t mean the cabinet number belongs to you. It is just a space temporarily allocated to you by the locker. When you take things out, the space will be taken back by the system. If you need to use it next time, the system will automatically allocate it to you.

So how do you solve this problem of swapping variables? There are two ways to do this. The first way is to define the two variables to be exchanged as global variables and let them occupy an address space during the program’s execution, so that no other variables can use the location. But this will be a waste of memory space, only used once, but to take up permanent. The second method is to use Pointers directly.

Let’s change the code to use Pointers.

void swap( u8 *m, u8 *n )
{
    u8 temp;   
    temp = *m;
    *m = *n;
    *n = temp;    
}
void main( void )
{
    u8 x = 10, y = 20;
    __asm( "sim" );                             // Disable interrupts
    SysClkInit();
    delay_init( 16 );
    LED_GPIO_Init();
    Uart1_IO_Init();
    Uart1_Init( 9600 );
    ADC_GPIO_Init();
    __asm( "rim" );                             // Enable interrupts
    while( 1 )
    {
        LED = ~LED;

        printf( "x = %d,y = %d\r\n", x, y );
        sawp( &x, &y );
        printf( "---> x = %d,y = %d \r\n\r\n\r\n", x, y );

        delay_ms( 200); }}Copy the code

The ampersand is used when passing arguments to swap. Print the output

The x and y values have been successfully swapped. Now print the interchangeables inside the subfunctions as well.

void swap( u8 *m, u8 *n )
{
    u8 temp;
    
    printf( "in: m = %d,n = %d\r\n", m, n );
    temp = *m;
    *m = *n;
    *n = temp;
    
    printf( "in: ---> m = %d,n = %d \r\n\r\n\r\n", m, n );
}
Copy the code

From the output, how can m and n be 1021 and 1020 respectively? What is this value? Direct single step trial.

In the main function, the address of x becomes 0x0003FD and the address of y becomes 0x0003FC. And then we go to the subfunctions.

It can be seen that the value of m is 0x03FD, the value of n is 0x03FC.*m is 0x0A, which is 10, and *0x14 is 20. Next, start swapping values.

You can see that m and n are the same, butM andThe n values are swapped. So let’s go back to our main function.

And then the values of x and y in the main function are swapped.

What are the 1021 and 1020 printed by the serial port just now? The hexadecimal value of 1021 is 0x03FD and the hexadecimal value of 1020 is 0x03FC. That is, the value of m printed just now is the same as the address of x, and the value of n is the same as the address of y.

So why do m and n become x and y addresses, and star m and star n become x and y values. This is where we get to the essence of Pointers. Inside the memory, it does not know any variables or Pointers. For the storage space, it only has addresses and values. That is, what value is stored at what address. For ordinary variables, the name of the variable is compiled by the compiler as an address, meaning that x and y are aliases for its own address. The values of x and y are the corresponding values in the address.

When operating on ordinary variables x and y, the system operates on their values by default. A pointer, on the other hand, takes the address as its value by default, and when manipulating the pointer, it takes the address by default. In order to distinguish a normal variable from a pointer, if you want to use a pointer, you need to put a label on it that tells the system that this is a special variable, that it operates directly on addresses, not values.

So when I’m defining a pointer and I put an asterisk in front of the variable, that tells the system that I’m special. Let’s say I define an int star m. That tells the system, when I default to m, you give me its address, not its value. When I want to take a value, I need to add a label *m, telling the system that I’m taking the value, not the address, this is a special case, don’t give me the default address.

When to pass regular variable to a pointer, because direct operation variables by default is normal value, and pointer storage address, so when the common variables and transfer data pointer, also want to add a label to ordinary variable &, the symbol system, I don’t now the default values, what I want is a special case of address.

So when you pass x and y to a pointer, you put an ampersand in front of it.

swap( &x, &y ); Swap (u8 *m, u8 *n);

The default pointer is the address, plus the asterisk (*) is the value. So isn’t that the same thing as *m = &x?

Since the subfunction is defined and passed together, one step is omitted. The standard operation should be.

Int * m;

m=&x;

Define a pointer, and then pass the special case of the ordinary variable, that is, take the address of the ordinary variable, to the default case of the pointer. So m by default represents the address of the value x, which is *m.

If you define and assign variables in a single statement, the above code can be abbreviated as

int *m = &x;

So the top function swap(&x, &y); Pointers are defined and assigned in a single statement, swap(u8 *m, u8 *n); This is a common shorthand.

Operating *m inside swap is the same as operating x directly, operating *n is the same as operating Y directly, so swapping *m and *n is the same as swapping x and y.

When the pointer is not used by the subfunction exchange, the value of the variable is passed at this time, equivalent to a copy of the value of the variable, to the subroutine.

With Pointers, you give the address of the variable directly to the child function. It’s just another alias for the variables x and y. When you manipulate an alias, you’re manipulating x directly.

Thus, Pointers are just a way of aliasing variables for the convenience of programming, the equivalent of having a key in your locker. If someone else has the key, they can open your locker. So Pointers can be dangerous to use.

If there is critical data in the system, then the system is at risk when the external function modifies the data if that data is passed as a pointer to the external function. It is possible that an external function modifies a value that is illegal, and its own system crashes. Therefore, it is important to pay attention to security issues when using Pointers.

From the above example, I believe we have a deeper understanding of Pointers. It is a special case that is set to facilitate manipulation of variables, which are labeled variables. As for what pointer variable, variable pointer, that is the name, do not understand these concepts do not bother, as long as when using, know how to use the line.