Deep reading: C language pointer, from bottom principle to fancy skills, graphic + code analysis

Time:2021-8-13
Deep reading: C language pointer, from bottom principle to fancy skills, graphic + code analysis

The following article is from the official account IOT town of Internet of things.  , Author Daoge

 

preface

 

If you ask what is the most important and powerful concept in C language, the answer will be pointer!

Powerful, which means easy and efficient use, but also means that the syntax is complex and error prone. Good pointer can greatly improve code execution efficiency and save system resources; If it is not used well, the program will be full of traps and vulnerabilities.

In this article, let’s talk about pointers. Start from the memory storage space at the bottom to the various pointer use skills at the application layer, step by step, peel the cocoon, and explain in the most straightforward language, so that you can enjoy it at one time.

explain:In order to facilitate explanation and understanding, the address of the memory space of the drawing in this paper is written casually, and the address alignment should be followed in the actual computer.

 

The essence of variables and pointers

 

2.1 memory address

After we write a program source file, the compiled binary executable file is stored on the hard disk of the computer. At this time, it is a static file, which is generally called a program.

When this program is started, the operating system will do the following things:

  • Copy the contents of the program (code segment and data segment) from the hard disk to the memory;
  • Create a data structure PCB (process control block) to describe various information of the program (e.g. used resources, open file descriptors…);
  • Locate the address of the entry function in the code segment and let the CPU execute from this address.

When a program begins to be executed, it becomes a dynamic state, commonly known as a process.

Memory is divided into physical memory and virtual memory. The operating system manages and packages the physical memory. Our developers are facing the virtual memory provided by the operating system.

These two concepts do not hinder the understanding of the article, so they are uniformly called memory.

In our program, variables are defined and used by a variable name.

Variable itself is a real thing. Variable name is an abstract concept used to represent this variable. For example: I am a real person, and I exist objectively on this earth. Brother Tao is a name I give myself. This name is taken arbitrarily, as long as I feel good. If I like, I can also name brother bird, brother long, etc.

So, after we define a variable,Where is this variable? That is the data area of memory.

Memory is a large storage area, which is divided into small spaces by the operating system. The operating system manages memory through addresses.

The smallest storage unit in memory is bytes (8 bits), and the complete space of a memory is composed of bytes one by one.

In the above figure, each small grid represents a byte, but it seems that we do not draw the memory model in books. The more common drawing method is as follows:

That is, draw the space of four consecutive bytes together, which is easy to express and understand, especially when going deep into the knowledge related to code alignment( I think the root cause should be: everyone draws like this, which is already pleasing to the eye ~ ~)

2.2 32 Bit and64 bit system

We usually say that the computer is 32-bit and 64 bit, which refers to the maximum storage length of the register in the computer CPU. If the maximum storage of 32-bit data in the register is 32-bit system.

In a computer, data is usually accessed back and forth between hard disk, memory and registers. The CPU connects the components through three kinds of buses:Address bus, data bus and control bus.The width of the address bus determines the addressing capability of the CPU, that is, the maximum address range that the CPU can reach.

Just now, the memory is managed by address. If the CPU wants to access a data from an address space in the memory, the CPU needs to output the address of the storage unit on the address bus.

If the width of the address bus is 8 bits, the maximum address space that can be represented is 256 bytes, and the largest storage unit in the memory can be found is 255 (starting from 0). Even if the actual space of the memory module is 2G bytes, the CPU cannot use the following memory address space. If the width of the address bus is 32 bits, the maximum address that can be represented is the 32nd power of 2, that is, 4G bytes of space.

[note]This is only to describe the concept of address bus. The address calculation method in the actual computer is much more complex. For example, segmentation, paging and offset are used to locate the actual physical memory in the virtual memory. There are large pages and small pages in the paging. Interested students can check the relevant data by themselves.

2.3 variables

In C programs, we use variables to “represent” a data and function names to “represent” a function. Variable names and function names are mnemonics used by programmers. Variables and functions can only be used by the CPU after they are finally put into the memory, and all the information (code and data) in the memory are stored in binary form. The computer will not distinguish which is code and which is data according to the format. When accessing memory, the CPU needs the address, not the variable name and function name.

Here comes the question:In the program code, the variable name is used to refer to the variable, and the variable is stored in memory according to the address. How do the two map (Associate)?

The answer is:compiler!When compiling C program files in text format, the compiler will run according to the target running platform (that is, where the compiled binary program runs? Is it an x86 computer? Or the development board of arm platform?) To arrange various addresses in the program, such as the address loaded into memory, the entry address of the code segment, etc. at the same time, the compiler will convert all variable names in the program into the storage address of the variable in memory.

Variables have two important attributes:Type of variable and value of variable

Example: a variable is defined in the code

int a = 20;

The type is int and the value is 20. The storage model of this variable in memory is:

We use the variable name a in the code. When the program is executed, it represents the data in the storage unit corresponding to the 0x11223344 address.

Therefore, it can be understood that the variable name a is equivalent to this address 0x11223344. In other words, if we can know in advance that the compiler arranges the variable a in the cell of address 0x11223344, we can directly operate the variable with this address value in the program.

In the figure above, the value of variable a is 20, which occupies four grids in memory, that is, four bytes. Why 4 bytes? The C standard does not stipulate that variables of each data type must occupy several bytes, which is related to the specific machine and compiler.

For example, in a 32-bit compiler:

Char: 1 byte;
Short int: 2 bytes;
Int: 4 bytes;
Long: 4 bytes.

For example, in a 64 bit compiler:

Char: 1 byte;
Short int: 2 bytes;
Int: 4 bytes;
Long: 8 bytes.

For ease of description, the following takes 32 bits as an example, that is, int variables occupy 4 bytes in memory.

In addition, four consecutive bytes from low address to high address, 0x11223344, 0x11223345, 0x11223346 and 0x11223347, are used to store the value 20 of variable a.

In the figure, it is represented by hexadecimal. The decimal value 20 is converted to hexadecimal: 0x00000014. Therefore, the four bytes 0x00, 0x00, 0x00 and 0x14 are stored from the start address in turn (the storage order involves the problem of the size end and does not affect the text understanding).

According to this diagram, if you want to know where variable a is stored in memory in the program, you can use the address operator &, as follows:

printf("&a = 0x%x \n", &a);

This sentence will print out: & A = 0x11223344.

Consider a 32-bit system: how many bytes does the pointer variable occupy?

2.4 pointer variables

Pointer variables can be understood in two levels:

  • The pointer variable is first a variable, so it has all the attributes of the variable: type and value. Its type is a pointer, and its value is the address of other variables.   Since it is a variable, you need to allocate a storage space for this variable in memory. In this storage space, the addresses of other variables are stored.
  • The data type pointed to by the pointer variable is determined when the pointer variable is defined. For example: int * p; It means that the pointer points to an int type of data.

First, to answer the question just now, in a 32-bit system, a pointer variable occupies 4 bytes of space in memory. Because when addressing the memory space, the CPU uses a 32-bit address space (4 bytes), that is, the address of a memory unit can be stored in 4 bytes. The value in the pointer variable stores the address, so it needs 4 bytes of space to store the value of a pointer variable.

Example:

int a = 20;

int *pa;

pa = &a;

printf("value = %d \n", *pa);

The storage model in memory is as follows:

For the pointer variable PA, first of all, it is a variable, so there needs to be a space in memory to store this variable, and the address of this space is 0x11223348;

Secondly, the content stored in this memory space is the address of variable a, and the address of a is 0x11223344, so the value 0x11223344 is stored in the address space of pointer variable PA.

The two operators & and * are described here:

&: get address operator, used to get the address of a variable. In the above code, &a is used to obtain the storage address of variable a in memory, that is 0x11223344.

*: this operator is used in two scenarios: when defining a pointer and when obtaining the value of the variable pointed to by a pointer.

  • int pa; The variable PA defined in this statement is a pointer. The previous int indicates Pa. this pointer points to a variable of type int. However, at this time, we do not assign a value to PA, that is, the value in the four bytes of the storage unit corresponding to PA is uninitialized, which may be 0x00000000 or any other number, which is uncertain;
  • The * in the printf statement means to obtain the value of the int type variable pointed to by PA. the scientific name is dereference. We just need to remember to obtain the value of the pointed variable.

2.5 operation pointer variable

The operation of pointer variables includes three aspects:

  • Operate the value of the pointer variable itself;
  • Obtain the data pointed to by the pointer variable;
  • What data type to use / interpret the content pointed to by the pointer variable.

The value of the pointer variable itself

int a = 20; This statement defines variable A. in subsequent code, as long as you write a, it means to operate the value stored in variable A. There are two kinds of operations: read and write.

printf(“a = %d \n”, a); This statement is to read the value in variable a, of course 20;
a = 100; This statement is to write a value of 100 to variable a.

Similarly, int * Pa; Statement is used to define the pointer variable Pa. in subsequent code, as long as PA is written, it means to operate the value in the variable PA:

printf(“pa = %d \n”, pa); This statement is to read the value in the pointer variable PA, of course 0x11223344;
pa = &a; This statement is to write the new value to the pointer variable PA.

Again, the pointer variable stores the address. If we can know in advance that the address of variable a is 0x11223344, we can also assign the value in this way: PA = 0x11223344;

Think about it, if you execute this statement printf (“& PA = 0x% X \ n”, & PA);, What will the print result be?

As mentioned above, the operator & is used to get the address, then & PA means to get the address of the pointer variable Pa. the memory model above shows that the pointer variable PA is stored in the address 0x11223348, so the print result is: & PA = 0x11223348.

Gets the data pointed to by the pointer variable

The data type pointed to by the pointer variable is defined, that is, the data type pointed to by the pointer PA is int, so printf (“value =% d \ n”, * PA) is executed; Statement, first know that PA is a pointer in which an address (0x11223344) is stored, and then obtain the value in the storage space corresponding to this address (0x11223344) through the operator *; Also, when defining PA, it has been specified that the value it points to is of type int, so we know that the address 0x11223344 stores data of type int.

What data type is used to use / interpret the content pointed to by the pointer variable

The following code:

int a = 30000;

int *pa = &a;

printf("value = %d \n", *pa);

According to the above description, we know that the print result of printf will be value = 30000, and the conversion from 30000 decimal to hexadecimal is 0x00007530. The memory model is as follows:

Now let’s do this test:

char *pc = 0x11223344;

printf("value = %d \n", *pc);

When the pointer variable PC is defined, it indicates that the data type it points to is char type, and the address stored in the PC variable is 0x11223344. When * PC is used to obtain the pointed data, the data at the address 0x11223344 will be read according to the char format, so value = 0 will be printed (in the computer, ASCII code is stored with equivalent numbers).

This example illustrates an important concept: everything in memory is a number. How to operate (interpret) the data in a memory address is completely told to the compiler by our code.

In this example just now, although the value of the integer variable a is stored in the space of 4 bytes from the address 0x11223344, it is completely legal for us to let the PC Pointer use / interpret the content at this address according to char data.

The above content is the most fundamental mental method of the pointer. After understanding this mental method, the rest is the problem of more knowledge and practice.

Some related concepts of pointer

 

3.1 const attribute

Const identifier is used to represent the immutable properties of an object, such as definition:

const int b = 20;

In the following code, the value of variable B cannot be changed. The value in B is always 20. Similarly, if const is used to modify a pointer variable:

int a = 20;

int b = 20;

int * const p = &a;

The memory model is as follows:

Const here is used to modify the pointer variable P. according to the nature of const, it can be concluded that P is fixed after being defined as the address of variable a and can no longer be changed. That is, only the address 0x11223344 of variable a can be stored in the pointer variable PA. If you write P = & B; in the following code;, An error will be reported during compilation, because P cannot be changed and can no longer be set to the address of variable B.

However, the value of the variable a pointed to by the pointer variable p can be changed, that is: * P = 21; This statement is legal because the value of pointer P has not changed (still the address 0x11223344 of variable C), but the value stored in variable C.

Distinguish it from the following code:

int a = 20;

int b = 20;

const int *p = &a;

p = &b;

The const here is not next to P, but next to the type int, which means that the const symbol is not used to modify P, but to modify the variable to which p points. So, if we write P = & B; It is legal to assign the address of variable B to pointer P, because the value of P can be changed.

However, this statement * P = 21 is illegal, because const in the definition statement limits the data obtained through pointer P, which cannot be changed and can only be read. This property is often used in function parameters. For example, the following code is used to calculate the CRC check of a piece of data. This function only needs to read the original data and does not need (nor can) change the original data. Therefore, it is necessary to use the const modifier on the formal parameter pointer:

short int getDataCRC(const char *pData, int len)

{

    short int crc = 0x0000;

    //Calculate CRC

    return crc;

}

3.2 void pointer

The keyword void is not a real data type. It embodies an abstraction and indicates that it is not any type. There are generally two usage scenarios:

  • Return value and formal parameters of function;
  • When defining a pointer, it does not specify the type of the data it refers to, which means that it can point to any type.

Pointer variables are also variables. Variables can be assigned to each other, so pointer variables can also be assigned to each other, for example:

int a = 20;

int b = a;

int *p1 = &a;

int *p2 = p1;

Variable a is assigned to variable B and pointer P1 is assigned to pointer P2. Note that their types must be the same: both a and B are int types, and P1 and P2 point to int types, so they can be assigned to each other. What if the data types are different?Cast is required。 For example:

int a = 20;

int *p1 = &a;

char *p2 = (char *)p1;

The memory model is as follows:

The P1 pointer points to int data. Now I want to assign its value (0x11223344) to P2. However, since the data type it points to is specified as char when defining the P2 pointer, it is necessary to forcibly type convert the pointer P1, that is, treat the data at the address 0x11223344 as char data, and then assign it to the P2 pointer.

If we use void * P2 to define the P2 pointer, there is no need to cast when assigning values, for example:

int a = 20;

int *p1 = &a;

void *p2 = p1;

The pointer P2 is of void * type, which means that any type of pointer can be assigned to P2, but the reverse operation is not allowed, that is, the void * type pointer cannot be directly assigned to other determined types of pointers, but must be forcibly converted to the data type pointed to by the assigned pointer. As shown in the following code, P2 pointer must be forcibly converted to int * type and then assigned to P3 pointer:

int a = 20;

int *p1 = &a;

void *p2 = p1;

int *p3 = (int *)p2;

Let’s look at a system function:

void* memcpy(void* dest, const void* src, size_t len);

The first parameter type is void *, which reflects the true meaning of the system for memory operation: it doesn’t care what data type the pointer passed by the user points to, but stores the data one by one in the space corresponding to this address.

The same is true for the second parameter. In addition, the const modifier is added, which indicates that the memcpy function only reads data from the SRC pointer and does not modify the data.

3.3 null pointer and wild pointer

A pointer must point to a meaningful address before it can operate on the pointer. If the address value stored in the pointer is a random value or an invalid value, the operation pointer is very dangerous. Generally, such a pointer is called a wild pointer, which is the source of many pointer related bugs in C code.

Null pointer: a pointer that does not point to anything

After defining a pointer variable, if there is no assignment, what is stored in the pointer variable is a random value, which may point to any address space in memory. At this time, you must not write to this pointer, because it may point to the code segment area in memory or the operating system area in memory.

Generally, a pointer variable is assigned null to represent a null pointer. In C language, null is essentially ((void *) 0), and in C + +, null is essentially 0. In the standard library header file stdlib. H, there are the following definitions:

#ifdef __cplusplus

     #define NULL    0

#else    

     #define NULL    ((void *)0)

#endif

Wild pointer: pointer whose address has expired

As we all know, the local variables in the function are stored in the stack area, and the memory space applied through malloc is located in the heap area, as shown in the following code:

int *p = (int *)malloc(4);

*p = 20;

The memory model is:

It is legal to apply for 4 bytes of space in the heap area, and then force the type to be converted to int *, assign it to the pointer variable p, and then set the value in this address to 14 through * P. If * P is used to operate this address after releasing the space pointed to by the P pointer, it is very dangerous, because this address space may have been allocated to other codes by the operating system. If the data in this address is forcibly operated and the program collapses immediately, it will be our greatest luck!

int *p = (int *)malloc(4);

*p = 20;

free(p);

//After free, you can no longer manipulate the data in the P pointer.

p = NULL;  //  You'd better add this sentence.

Pointers to different data types

 

4.1 numerical pointer

Through the above introduction, the pointer to a numeric variable has been clearly understood. What needs to be noted is the data type pointed to by the pointer.

4.2 string pointer

There are two representations of strings in memory:

  • It is represented by an array, for example: char name1 [8] = “Zhangsan”;
  • It is represented by a char * pointer, for example: char * Name2 = “Zhangsan”;

Name1 occupies 8 bytes in memory, in which 8 characters of ASCII code value are stored; Name2 occupies 9 bytes in memory, because in addition to storing 8-character ASCII code values, an additional ‘\ 0’ is stored after the last character ‘n’ to identify the end of the string.

For strings, it is very convenient to operate with pointers, such as variable string Name2:

char *name2 = "zhangsan";

char *p = name2;

while (*p != '
char *name2 = "zhangsan";
char *p = name2;
while (*p != '\0')
{
printf("%c ", *p);
p = p + 1;
}
') { printf("%c ", *p); p = p + 1; }

In the judgment condition of while, check whether the character pointed to by the P pointer is the terminator ‘\ 0’.

After printing the character currently pointed to, the pointer is automatically incremented. Because the data type pointed to by pointer P is char, and each char occupies a byte in memory, pointer P points to the next storage space after incrementing by 1.

You can also write 2 statements in the loop body as 1 statement:

printf("%c ", *p++);

If the data type pointed to by a pointer is int, execute P = P + 1; After that, the address value stored in pointer P will increase by 4, because an int data occupies 4 bytes in memory, as shown below:

Consider a question: can void * pointers be incremented? The following test codes:

int a[3] = {1, 2, 3};

void *p = a;

printf("1: p = 0x%x \n", p);

p = p + 1;

printf("2: p = 0x%x \n", p);

The printing results are as follows:

1: p = 0x733748c0 

2: p = 0x733748c1

Note: when the void * pointer is self incremented, it is calculated according to the span of one byte.

4.3 pointer array and array pointer

These two statements are often confused. At least I am. Let’s look at these two statements first:

int *p1[3];   //  Pointer array

int (*p2)[3]; //  Array pointer

Pointer array

In the first statement: brackets [] have high priority, so they are combined with P1 first to represent an array. There are three elements in this array. These three elements are pointers, which point to int data.

It can be understood as follows: if char P [3] is defined, it is easy to understand that this is an array with three char elements. Replacing char with int * means that the element type in the array is int * (pointer to int data). The memory model is as follows (Note: the addresses pointed to by the three pointers are not necessarily continuous):

If you assign values to the elements in the pointer array, you need to assign the addresses of the variables to the pointer elements one by one:

int a = 1, b = 2, c = 3;

char *p1[3];

p1[0] = &a;

p1[1] = &b;

p1[2] = &c;

Array pointer

In the second statement: parentheses combine P2 with *, indicating that P2 is a pointer, which points to an array. There are three elements in the array, and the type of each element is int. It can be understood in this way: if there is this definition int p [3], it is easy to understand that this is an array with three char elements. Then replace the array name P with * P2, that is, P2 is a pointer to this array. The memory model is as follows (Note: the address pointed to by the pointer is an array, in which three elements are continuously placed in memory):

Earlier, we talked about the address operator &, which is used to get the address of a variable. There are special circumstances for everything. For obtaining addresses, the & operator is not required in the following cases:

  • When a string literal is used as an R-value, it represents the first address of the string in memory;
  • The array name represents the address of the array, which is also equal to the address of the first element of the array;
  • The function name represents the address of the function.

Therefore, for the following code, the print results of the three printf statements are the same:

int a[3] = {1, 2, 3};

int (*p2)[3] = a;

printf("0x%x \n", a);

printf("0x%x \n", &a);

printf("0x%x \n", p2);

Think about it, if you execute P2 = P2 + 1 on the P2 pointer here; Operation, how much will the value in P2 increase?

The answer is 12 bytes. Because P2 points to an array, which contains 3 elements and each element occupies 4 bytes, the array occupies a total of 12 bytes in memory. Therefore, P2 will skip 12 bytes after adding 1.

4.4 two dimensional arrays and pointers

One dimensional array is composed of multiple memory units continuously distributed in memory, and two-dimensional array is also composed of multiple memory units continuously distributed in memory. From the perspective of memory, one-dimensional array and two-dimensional array have no essential difference.

Similar to one-dimensional array, the array name of two-dimensional array represents the first address of the first element in the first dimensional array of two-dimensional array, which is explained by code:

int a[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; //  Two dimensional array

int (*p0)[3] = NULL;   //  P0 is a pointer to an array

int (*p1)[3] = NULL;   //  P1 is a pointer to an array

int (*p2)[3] = NULL;   //  P2 is a pointer to an array

p0 = a[0];

p1 = a[1];

p2 = a[2];

printf("0: %d %d %d \n", *(*p0 + 0), *(*p0 + 1), *(*p0 + 2));

printf("1: %d %d %d \n", *(*p1 + 0), *(*p1 + 1), *(*p1 + 2));

printf("2: %d %d %d \n", *(*p2 + 0), *(*p2 + 1), *(*p2 + 2));

The print result is:

0: 1 2 3 

1: 4 5 6 

2: 7 8 9

We take the first printf statement to analyze: P0 is a pointer to an array. The array contains three elements, and each element occupies four bytes in memory. Now we want to get the data in this array. If you directly add 1 to P0, P0 will span 12 bytes (equal to the value in P1). Therefore, you need to use the dereference operator *, turn P0 into a pointer to int, and then add 1 to get the int data in the array.

4.5 structure pointer

The basic data types in C language are predefined, and the structure is user-defined. Analogy can be made in the use of pointers. The only difference is that in the structure pointer, the – > arrow operator needs to be used to obtain the member variables in the structure, for example:

typedef struct 

{

    int age;

    char name[8];

} Student;




Student s;

s.age = 20;

strcpy(s.name, "lisi");

Student *p = &s;

printf("age = %d, name = %s \n", p->age, p->name);

It seems that there is no technical content. What if it is a structure array? For example:

Student s[3];

Student *p = &s;

printf("size of Student = %d \n", sizeof(Student));

printf("1: 0x%x, 0x%x \n", s, p);

p++;

printf("2: 0x%x \n", p);

The print result is:

size of Student = 12 

1: 0x4c02ac00, 0x4c02ac00 

2: 0x4c02ac0c

After P + + operation, the space P needs to cross is the size of a structure variable in memory (12 bytes), so p points to the first address of the second element in the array. The memory model is as follows:

4.6 function pointer

After compilation, each function becomes a set containing multiple instructions. After the program is loaded into memory, the instruction set is placed in the code area. The function name we use in the program represents the starting address of the instruction set.

Function pointer is still a pointer in essence, but the pointer variable stores the address of a function. What is the most important feature of a function? Can be called! Therefore, when a function pointer is defined and a function address is assigned to the pointer, the function can be called through the function pointer.

The following example code:

int add(int x,int y)

{

    return x+y;

}




int main()

{

    int a = 1, b = 2;

    int (*p)(int, int);

    p = add;

    printf("%d + %d = %d\n", a, b, p(a, b));

}

As mentioned earlier, the name of the function represents the address of the function, so the function name add represents the address of the addition function in memory. int (*p)(int, int); This statement is used to define a function pointer, which points to a function. The function must meet the following two points (scientific name: function signature):

  • There are 2 parameters of type int;
  • There is a return value of type int.

The add function in the code just meets this requirement. Therefore, add can be assigned to the function pointer P. at this time, P points to the address of the function stored in memory, and then the function can be called with the function pointer P.

In the example code, the function pointer P is defined directly. If you want to define two function pointers, do you need to define them like this?

int (*p)(int, int);

int (*p2)(int, int);

The parameters here are relatively simple. If the function is very complex, wouldn’t this definition be annoying? You can use the typedef keyword to define a function pointer type:

typedef int (*pFunc)(int, int);

Then pfunc P1, P2 in this way; It is much more convenient to define multiple function pointers. Note: only functions with the same signature as the function pointer type can be assigned to P1 and P2, that is, the number and type of parameters should be the same, and the return value should be the same.

Note: here are some details:

  • When assigning a function pointer, use p = & A; It is also possible;
  • When calling with a function pointer, use (* P) (a, b); It’s OK.

There is no special principle to explain here. In the end, the compiler helps us deal with the details here. Just remember it directly.

After the function pointer is fully understood, it is combined with the array: the function pointer array. The example code is as follows:

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

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

int mul(int a, int b) { return a * b; }

int divide(int a, int b) { return a / b; }




int main()

{

    int a = 4, b = 2;

    int (*p[4])(int, int);

    p[0] = add;

    p[1] = sub;

    p[2] = mul;

    p[3] = divide;

    printf("%d + %d = %d \n", a, b, p[0](a, b));

    printf("%d - %d = %d \n", a, b, p[1](a, b));

    printf("%d * %d = %d \n", a, b, p[2](a, b));

    printf("%d / %d = %d \n", a, b, p[3](a, b));

}

This statement is not easy to understand: int (* P [4]) (int, int);, First analyze the middle part. The identifier P is combined with brackets [] (high priority), so p is an array with 4 elements; Then the rest represents a function pointer, which means that the element type in the array is a function pointer, that is, the address of other functions. The memory model is as follows:

If it’s still hard to understand, go back to the essence of pointer: pointer is an address! What is stored in this address is not important at all. What is important is that you tell the computer what the content is. If you tell it that the content stored in this address is a function, the computer will call this function. So how do you tell the computer, that is, when defining pointer variables, that’s all!

summary

 

I have explained all the concepts, grammar and usage scenarios related to pointers I know. Just like the shopkeeper of a small pub, I present my good wine and food to you. I hope you are full of wine and food!

If the above content is too much to digest for the moment, the following two sentences will be served as dessert for you. In future programming, if you encounter pointer related confusion, think about these two sentences, which may make you enlightened.

  • The pointer is the address, and the address is the pointer.
  • A pointer refers to a space in memory. How to interpret / operate this space depends on the type of the pointer.

Another thing to ask is to learn any programming language,Be sure to find out the memory model, memory model, memory model!

 

If youyesC / C + + interested, want to learn programming, Xiaobian recommends oneC/C++Technical exchange group [Click to enter]!

Involving: introduction to programming, game programming, network programming, windows programming, Linux programming, QT interface development, hackers, etc