Pointers of C language [Super detailed explanation, take you to understand pointers layer by layer]

Table of contents

1. About pointers

2. Pointer type

1. Description of the access rights of integer pointers:

2. Description of the access rights of the character pointer:

3. The type of pointer determines how far forward or backward one step is

3. Knowledge about wild pointers

1. The cause of the wild pointer

① The pointer is not initialized

② Pointer out-of-bounds access

③The space pointed to by the pointer is released

2. How to avoid wild pointers

① The pointer needs to be initialized

②Pay attention to the problem that the pointer crosses the boundary

③The space pointed to by the pointer is set to NULL in time

④ Avoid returning the address of a local variable

⑤ Check the validity of the pointer before use

4. Constant pointers and pointer constants

1. Constant pointer

2. Pointer constant

Five, the operation of the pointer 

1. Pointer + - integer

2. Pointer-Pointer

3. Relational operations on pointers

6. Character pointer

Constant string (related knowledge)

Seven, pointer array

The meaning of the array name

8. Array pointer

Nine, array parameters, pointer parameters

1. One-dimensional array parameter passing

2. Two-dimensional array parameter passing

3. First-level pointer parameter passing

4. Secondary pointer parameter passing

10. Function pointer

function pointer renaming 

11. Array of function pointers


​​​​​​​

1. About pointers

First, the memory will be divided into small memory units, and each memory unit has a number, which is called an address . We also call an address a pointer , as shown in the following figure:


Note: Pointers refer to addresses, but pointers in our spoken language usually refer to pointer variables

A pointer variable is a variable used to store an address

We can modify the original value by modifying the value of the pointer variable, as follows:

The original value of a is 5, through the pointer variable pa, dereferencing can also change the value of a


Regarding the size of the pointer: the size is 4 bytes on the 32-bit platform , and 8 bytes on the 64-bit platform , and it will not change because of the change of the pointer type! The following is a demonstration in the 32-bit platform environment of VS2019:

 It can be seen from the observation that whether it is an integer pointer or a character pointer, the memory size is 4 bytes under the 32-bit platform.

Note: The 32-bit platform is composed of 32 bits, so 32 bits=4 bytes , so the pointer on the 32-bit platform is 4 bytes in size, and the same is true for 64-bit


2. Pointer type

The pointer type determines the access permissions of the pointer when it is dereferenced, for example:

Dereferencing an integer pointer accesses 4 bytes

Char pointer dereference accesses 1 byte

Let's specifically prove the access rights of these two types of pointers:

1. Description of the access rights of integer pointers:

a is 0x11223344 in hexadecimal, observe on the monitoring page that a is the same as *pa, both are values ; &a is the same as pa, both are addresses

 Observe the storage of a in memory at this time

Enter the address of a, and find that the memory is stored as 44 33 22 11. This involves the big and small endian storage we mentioned earlier. Friends who don’t know can go to the blog "Data Storage in C Language" to learn

Our VS uses little-endian storage , so it is 44 33 22 11, and then after running to *pa=0, we can observe the memory:

The value of a has been changed to 00 00 00 00, and the values ​​of 4 bytes have been changed 

2. Description of the access rights of the character pointer:

In the same order as before, as shown in the figure below:

The rest of the code remains unchanged, only the type of the pointer is changed

 Monitoring is the same as the memory section

When the code runs *pa=0

We found that for the character pointer type, dereferencing can only change 2 hexadecimal digits , that is, 8 bits , that is, 1 byte

The above two examples prove that different pointer access rights are different

3. The type of pointer determines how far forward or backward one step is

The specific meaning is that the pointer variable is added or subtracted by one, and its address changes, as follows:

The integer pointer variable pa and the character pointer variable pb are respectively defined ,  and %p is used to print the address respectively. Observing the running results shows that:

The addresses of pa and pb are the same, because both pa and pb point to the address of a

However, pa+1 and pb+1 are quite different. Pa+1 adds 4 to pa, while pb+1 only adds 1 to pb. From this example, it can be seen that the type of pointer is also determined, and the pointer points to How much distance can be covered by taking one step forward or backward.


3. Knowledge about wild pointers

The wild pointer is that the pointer points to an unknown position !

1. The cause of the wild pointer

① The pointer is not initialized

This kind of problem is very common, see the following code for details;

This error will also be displayed after running

 The code int* p is not initialized , so the address stored in the p variable does not point to the space of our current program, but to a random space in the memory , so it must be wrong to access this memory space with *p!

Here p is the wild pointer

② Pointer out-of-bounds access

This problem can be said to be very common. We usually encounter such problems when traversing arrays if we don’t pay attention:

We set the array arr, and make the pointer variable p equal to the address of the first element of the array. The array itself has 5 elements , but it loops 6 times in the for loop , i are 0 1 2 3 4 5, and i itself is 0~4 Yes, there is an extra 5 in the loop, which leads to illegal memory access , so the sixth number is printed as a random number, and this is also a problem of wild pointers. 

③The space pointed to by the pointer is released

This problem is also frequently encountered, see the following example for details:

The return value of the test function is the address of x. In the main function, the pointer variable p is used to receive the address of x, but the x variable enters the test function to create, and destroy it . At this time, change the value of *p, that is, use x address, it is illegal to access the memory , and it will also cause the problem of wild pointers

2. How to avoid wild pointers

① The pointer needs to be initialized

For example, in the above example, int* p cannot be directly used, it must be initialized, int a = 10; int* p = &a; so as to avoid the wild pointer problem

②Pay attention to the problem that the pointer crosses the boundary

When we use arrays, we must pay attention to the number of elements in the array and the number of times we loop , so as to avoid careless and out-of-bounds access

③The space pointed to by the pointer is set to NULL in time

When we do not use the pointer variable p , int* p = NULL; make it empty , and when we want to use p next, use the if statement:

if(p != NULL)  ......

Can well avoid wild pointers

④ Avoid returning the address of a local variable

Just like the third example of the cause of the wild pointer above, avoid returning the address of the local variable

⑤ Check the validity of the pointer before use

like if(p != NULL)  ...is checking the validity of the pointer


4. Constant pointers and pointer constants

There are a lot of constant pointers and pointer constants encountered in the topic. They are all related to the keyword const . Here are three examples to see if you can distinguish the difference:

const int *pa = &a;

int const *pa = &a;

int *const pa = &a;

1. Constant pointer

The concept of a constant pointer is: the value of the space pointed to by the pointer cannot be changed , and the value of the space pointed to by the pointer cannot be modified by dereferencing the pointer, but the pointing of the pointer can be changed

2. Pointer constant

The concept of pointer constant is: the pointer itself is a constant, that is, the point of the pointer cannot be changed , but the value of the space pointed to by the pointer can be changed , and the value of the space pointed to by the pointer can be changed by dereferencing


How to understand it better?

Let’s first look at the name of the constant pointer, which ends with a pointer , so it has the nature of a pointer, so it can be changed, and because it is a constant pointer, the value pointed to is constant, that is, it cannot be changed

Look at the name of the pointer constant, which ends with a constant , and should have some properties of a constant, so naturally the pointing of the pointer cannot be changed, only the value pointed to by the pointer can be modified.

So where in the code is const added to be called a constant pointer, and where is it added to be called a pointer constant?

Saying a little trick can quickly help everyone remember:

If const is on the left of *, it is a constant pointer

If const is on the right side of *, it is a pointer constant

As for why you want to remember it like this, you can think about it, * has the function of dereferencing in the pointer, then const is on the left of *, which can be understood as const modification * , that is, the value of the space pointed to by the pointer cannot be modified, but the point of the pointer is indeed unlimited

Then const is on the right side of *, and it is modified as a pointer variable , that is, the pointer variable cannot change its point, but the value of the space pointed to by the pointer is not limited

Then the above three examples obviously have the answer, just need to see whether the const modification is * or a pointer variable

Constant pointer: const int *pa = &a;, int const *pa = &a;

Pointer constant: int *const pa = &a;


Five, the operation of the pointer 

1. Pointer + - integer

Give an example to briefly illustrate the situation of adding and subtracting integers by pointers:

 The pointer variable p is the address of the first element of the array. Use the for loop to print each element of the array. The priority of * is higher than ++ . Therefore, after each execution of *p prints the elements in the array, p++ points to the next element of the array . Thus looping 5 times to print out 5 elements in the array

2. Pointer-Pointer

 The pointer-pointer operation gets the number of elements between the pointer and the pointer , of course, the premise is that the two pointers must point to the same space , see the example below;

The specific drawing is explained as follows:

As shown in the figure above, it represents the storage of five elements in the array in the memory, where p1 points to the address of the first element , and p2 represents the address of the fifth element . Using p2-p1 in the code means that it is between two pointers The number of elements between, it is clear that there are 4 elements , so the printed result is 4

3. Relational operations on pointers

The relational operation of pointers is to compare the size of pointers to achieve certain functions, such as:

 The circled is the relational operation of the pointer, and the picture drawn below can be clearly explained:

 p is the address of the first element of the array at the beginning, then through the comparison of p< &arr[5] , if it is satisfied, the element under the address will be printed, and then p++, the next round of comparison will be performed until p<&arr[5] is not satisfied

There may be friends here who want to ask. Didn’t the above say that you can’t cross the boundary? The array has only 5 elements, and the maximum subscript is 4. How can you use arr[5]? In fact, there is no boundary here. The above is used The out-of-bounds address has already illegally accessed the memory, and this address is only written out here, and the pointer variable p is used for relational operations, and this address is not used, so there is no problem of wild pointers , please rest assured♪(^∇^* )

Regulations: A pointer to an array element is allowed to compare with a pointer to the memory location after the last element of the array (as in the example above), but not to a pointer to a memory location before the first element


6. Character pointer

The character pointer is the address of the stored character, that is, the address of the character variable is put into the character pointer , for example as follows:

Define the pointer variable pa to store the address of a, change the value of *pa, and a will change accordingly 

Constant string (related knowledge)

If there is such a code: char* pa = "abcde"; "abcde" here is a constant string , "abcde" is stored in the constant area, and pa points to the address of a

The constant string cannot be modified. If someone wants to write a code similar to *pa='w', the program will crash, so we can add const in front for protection and write it as const char * pa = "abcde" (here is the type of constant pointer)

And this code does not store "abcde" in pa, but stores the address of a in the pointer variable pa. The following example can explain this knowledge more clearly:

  

 Dereference pa, print out and find that it is indeed character a, then pa+1, it should point to character b, dereference and print out is indeed the value of character b, if you want to print the entire string, then use %s directly , pa can be printed out

Here is a very classic example: You can think about whether p1 and p2, p3 and p4 are equal

The answer is: p1 and p2 are equal, p3 and p4 are not equal

 From the above content, we can see that "abcde" is a constant string, which is stored in the constant area, so both p1 and p2 point to this constant string , and both store the address of a, so p1 and p2 are equal

And p3 and p4 are the initialization of two arrays respectively , so p3 and p4 respectively represent the addresses of the first elements of the two arrays, so p3 and p4 are different


Seven, pointer array

When it comes to pointer arrays, you may hear less, but when it comes to vocabulary such as integer arrays and character arrays, you may hear more, so what exactly is a pointer array?

First talk about integer arrays and character arrays

Integer array : It can be seen from the literal meaning that an integer array is an array that stores integers , such as int arr[5]={1,2,3,4,5}, the arr array is an integer array, and the array has 5 elements, the type of each element in the array is int

Character arrays are also the same as integer arrays, and so on

So when it comes to this, the definition of pointer array is very simple, it is an array that stores pointers , for example int* arr[3]={&a,&b,&c}, the array has 3 elements, and each element in the array is Pointer, the type of each element is int* , such an array is called a pointer array

Here is a case to help you understand the practical application of pointer arrays better:

We first created 3 integer arrays arr1, arr2, arr3, and then created a pointer array arr to store the address of the first element of the three integer arrays (because the array name is the address of the first element of the array ), we will 3 integer The address of the first element of the type array is stored in the pointer array arr, so when you want to represent the elements in the integer array, it is equivalent to creating a two-dimensional array , which can be clearly shown in the following figure:

Each element in the arr array is of type int*, then arr[i] is the element in the arr array , that is, the address of the first element of the 3 integer arrays in the arr array, and arr[i][j] is 3 The integer element with subscript j in an integer array , so the application of this pointer array is equivalent to a two-dimensional array

Another representation method is *(arr[i]+j), arr[i] means the elements in the arr array , that is, the address of the first element of the 3 integer arrays, and +j means backward from the address of the first element Move the address of j elements , and then dereference and print them out, which is also an element of an integer array

Let's give an example of arr1 array, everyone will be more clear

This is the explanation of the two printing methods in the code, I believe everyone understands the reason!

The meaning of the array name

Speaking of arrays, then we have to talk about the meaning of the array name

Generally speaking, the array name is the address of the first element of the array

But there are also 2 exceptions:

1. sizeof (array name) , in this case, the array name is not the address of the first element of the array. At this time, the array name represents the entire array, and the calculation is the size of the entire array

2. & array name , in this case, the array name is not the address of the first element, but also the address of the entire array, and the address of the entire array is taken out

Draw a picture below to help you understand

 It can be seen from the figure that the values ​​of &arr[0], arr, and &arr are the addresses of the first element of the array, but the meanings they represent are different. If &arr[0] and arr are +1, an integer is skipped . type element , pointing to the address of the next integer element, and if &arr is +1, it skips an array and points to the address of the next position of element 8

 Through the code, you can clearly observe the conclusion just drawn

 The above are the knowledge points to be added about the meaning of the array name♪(^∇^*)


8. Array pointer

After talking about the pointer array, now let's talk about the array pointer. The pointer array is an array, so the array pointer is a pointer.

We have learned about integer pointers before, int* p=&a, is a pointer to an integer, storing the address of an integer variable; and a character pointer is a pointer to a character, storing the address of a character variable; then the array pointer naturally points to the array the pointer

Then give an example:

int* p1[3];

int (*p2)[3];

Of the two examples above, which one is an array pointer and which one is an array of pointers?

You can first observe p1 and p2, which are the array names we learned before, first look at the first int *p1[3]; because [] has a higher priority than *p1, when encountering square brackets, it will first match with square brackets Brackets are combined , so this is an array with 3 elements , and observing the int* in front of p1, you can know that the type of each element in this array is int* , so this is an array of pointers

Looking at the second int(*p2)[3]; p2 is first combined with *, p2 is a pointer variable, so this is a pointer to an array of size 3, and each element is of type int, so this is an array pointer

First, for integer pointers, int a = 5; int* pa = &a, given an integer variable a, take the address of a and place it in the integer pointer pa, where the pointer variable pa is of type int*, which can be considered It is int* pa After removing the variable name of pa, the rest is the type of this pointer

In the same way, int arr[3]={0}; int (*parr) [3] = &arr ;Given an integer array arr with 3 elements, then the address of the array should be placed in the array Inside the pointer parr, then the question is, what is the type of the array pointer parr ? It is very simple, and it can be obtained by removing the name of the pointer variable from int (*parr) [3], that is, int (*) [3], which can be obtained according to the type. Such a pointer points to an array, and the array has 3 elements. Each elements of type int

Here is an example to illustrate:

According to the above, pa is the address of variable a, and its type is int*, so when printing the address, the address of pa+1 is increased from 00A8FB54 to 00A8FB58, which increases the size of 4 bytes (the size of an integer), while parr It is the address of the storage array arr, and the type is int (*) [3], so of course, the address of parr+1 is changed from 00A8FB34 to 00A8FB40 compared to the address of parr, which increases the size of 12 bytes (three integers Size) Because the array pointed to by this pointer variable has three elements, and each element is of type int, so parr+1 skips the addresses of 3 integer variables.


Nine, array parameters, pointer parameters

In the code, you need to pass arrays and pointers to functions, which involves the concept of array parameters and pointer parameters

1. One-dimensional array parameter passing

There are one-dimensional arrays arr1 and arr2, and parameters are passed to test1 and test2, so how should the formal parameters of test be written?

Because passing the name of the array is equivalent to passing the address of the first element of the group, the formal parameter can be written in the form of an array or in the form of a pointer

Arr1 array parameter passing:

The form of the array: void test1(int arr1[]) or void test1(int arr1[5])

Each element type is int, so the pointer is int*

The form of the pointer: void test1(int* p1)

Arr2 array parameter passing:

The form of the array: void test2(int* arr2[]) or void test2(int* arr2[15])

Each element type is int*, so the pointer is int**

The form of the pointer: void test2(int** p2)

2. Two-dimensional array parameter passing

The basis of one-dimensional array parameter passing, there are two forms of two-dimensional array parameter passing

Pay attention to the two-dimensional array parameters, the row can be omitted, the column can not be omitted, because if the column is omitted, you don’t know the specific number of each column, which will cause problems in the memory arrangement, because the memory is the first row and the second row Continuously stored

Array format: void test (int arr[][3]) or void test (int arr[2][3])

The array name is the address of the first element of the array, and the address of the first element of the two-dimensional array is the address of the first row of the one-dimensional array, so the address of the first row of the array is an array pointer: int(*)[3], the formal parameter should be Array pointer p: int(*p)[3]

The form of the pointer: void test(int(*p)[3])

3. First-level pointer parameter passing

First-level pointers pass parameters, pass first-level pointers, then use first-level pointers to receive

It can also be reversed. If the parameter of test is int* ptr, then the function can receive the following three parameters

①test(arr)②test(p)③There is an int a = 0; the address of a can be passed to test(&a)

4. Secondary pointer parameter passing

The second-level pointer is passed as a parameter, and the second-level pointer is passed, then the second-level pointer is used to receive

Then similarly, if the parameter of test is int** ptr, then the function can receive the following three parameters

One and two are easy to understand. Look at the third one. The arr array has 5 elements, and each element is of type int*. Passing in arr is equivalent to passing in the address of the first element of the array, and the type of the first element is int*. Then The address of int* is int**, which also meets the requirements 


10. Function pointer

A function pointer is a pointer to a function. Here is an example of a function pointer

Here p is the function pointer

First, p and * are combined in parentheses, indicating that p is a pointer, and the parameter types of the function are int and int in parentheses. 

The same here, p is a function pointer, but here is print instead of &print, because the meanings of these two ways are the same 

When calling, both of the above can be transferred to the function

function pointer renaming 

If the type of a function pointer is int (*)(int,char) and it is too long and complicated, you can use typedef to rename it, but the renaming here is different from the previous naming format, and the name should be placed in * The latter, namely: typedef int (*p)(int,char), the meaning of the code is to rename the function pointer type int (*)(int,char) to p


11. Array of function pointers

The function pointer array is an array, and the function pointers are stored in the array, as follows:

int(*ptr[3])(int, int) is the use of the function pointer array, ptr is the function pointer array, remove the array name ptr and [3], we can see that each element in the array is int(*)(int, int ) function pointers, so it is called an array of function pointers


That's all about pointers, if you are interested, you can do more research. O(∩_∩)O

Guess you like

Origin blog.csdn.net/m0_64411530/article/details/125584522