When learning C language, beginners usually encounter two bottlenecks: "recursion" and "Pointer ". When talking about these two knowledge points, university teachers often follow the instructions and do not examine the problem from the perspective of a beginner, nor analyze its internal mechanism. I am going to post a series of technical articles here, hoping to clearly describe the concept of "pointer" in C language and hope that beginners can benefit from it. Here, I strongly recommend the book "Pointers On C" written by Jian th A. Reek.
1. Memory and address
Beginners are always familiar with memory. First, in daily life, we will always discuss the memory size of a certain device, whether to add a memory stick, and so on. But what is the memory? Here, I do not want to go deep into the nature of memory and the differences in the structures of different operating systems. Here, you only want beginners to know the following three questions:
(1) memory is the place where the computer stores data during running.
(2) memory is divided into "countless" small regions. The size of each region varies in different environments. It may be 1 byte, 2 byte, or multiple bytes.
(3) Each small area has a unique identifier, that is, the address (pointer) We call later ).
(4) Each cell contains a value, which can be an integer, floating point, complex, and so on.
We can use the image to represent the memory structure:
100 104 108 112 116
|
------------------------------------------------------------------------
(542) (3.14) ('A') (12323) (-12)
, The above indicates the memory address, and the content in the brackets below indicates the memory value under the address. To access these memory values, you only need to know the memory address. However, how do we know the memory address of the content we want to access? Indeed, it is almost impossible to remember these memory addresses, so the compiler allows us to access them in the form of variables, so this figure can be changed to the following:
Total_count Pi m_ch money number
|
------------------------------------------------------------------------
(542) (3.14) ('A') (12323) (-12)
We change the memory address into a variable that we can easily remember, so that we can easily access the data in the memory when writing a program. However, please remember that this is only because the compiler has helped us with optimization. The real compiled machine code accesses the data in the memory through the real memory address, that is, addressing, readers who are interested in the addressing process of computers can refer to books on computer composition principles or assembly languages.
2. Value and type
First, let's look at the following expressions of the operator:
Int total_count = 542;
Float Pi = 3.14;
Int * p1 = & total_count;
Char * p2 = & m_ch;
The first two expressions are well understood. We declare an integer and a floating point variable, and assign values to 542 and 3.14 respectively. What do the following two expressions mean?
Let's just remember: In the expression for applying for a variable, if the * sign appears after the type, the variable is called a pointer or a pointer variable. Pointer variables can be divided into many types, such as integer pointers, floating point pointers, and character pointers.
Now that we know the pointer concept, what is the pointer?
A pointer is also a variable, that is, a pointer variable. It is essentially no different from other variables. However, the pointer variable can only save one value, that is, the address.
Maybe you will ask, since the pointer stores the address, why should we divide the pointer into so many types? Should there be an integer pointer for the integer value and a struct pointer for the struct value? Why can't I use a pointer to include all the addresses? Are the addresses of different types of variables different?
I have not read the official explanation for this problem. First of all, I can be sure that there is no difference in the memory address theoretically. No matter what type of variable memory is used to save, it is essentially a region composed of BITs, and the memory address is certainly not different. From the perspective of pure technology, the author thinks that the compiler can establish a system completely, store the addresses of different variables with a unified pointer type, which does not affect the running of the program. However, the compiler does not do this because of security considerations. When a programmer intentionally or unintentionally assigns values to the memory content pointed by two different types of pointers, if the compiler cannot perform a check beforehand, an exception may occur during the program running, for example, a very serious buffer overflow error occurs.
3. Indirect pointer accessors
Okay, let's move the problem back to these expressions. The problem is what are saved in the p1 and p2 variables. Take p1 as an example. Maybe you can understand this. Because p1 is a pointer variable, its value should be the same as the value in the memory it points to, so p1 = 542. this understanding seems very logical, but it is a big mistake. Although p1 is special, pointer variables are also variables, which are not smart enough to automatically complete a very complex automatic indirect access operation. The actual value of p1 is 100, that is, the address of the variable total_count. However, please note that 100 is not a traditional integer of 100, for example, int * p1 = 100. This is an incorrect expression, because the integer value cannot be directly assigned to the pointer variable, make the following changes to int * p1 = (int *) 100;
That is to say, if we output the p1 value, it will be a string of inexplicable numbers. Of course, we are usually not interested in these inexplicable addresses. We are more interested in the information behind these addresses. So we can access int count = * p1 in this way;
Wait... the reader may be confused when reading this,
Int * p1 = & total_count; int count = * p1; readers may be confused about the number and number of so many numbers. Let's clarify our ideas.
In int * p1 = & total_count:
* P1 is only an identifier, indicating that p1 is a pointer. You can directly access p1 when you access this pointer later. The accessed value is an address code.
& Total_count represents an address. Adding the & symbol before any variable (including pointers) will represent the address of the memory where the variable is located. & P1 indicates the address of the memory where the pointer is located, that is, the pointer (which will be detailed later). In fact, there are some doubts, because the pointer itself is also a variable, therefore, it is essentially no different from other variables.
In int count = * p1;, p1 is the same as previously mentioned. The value of access to it is an address, and * is somewhat different from the previous * number, the previous "*" is only an identifier, indicating that the currently applied variable is a pointer. Here, "*" gives it a new name, called the indirect access character of the pointer. It sounds awkward. Simply put, adding the * sign before a pointer variable can access the actual memory value represented by the pointer.
Here we can teach beginners a little trick. When we understand a pointer-related statement, the words "Pointer" and "Address" can be exchanged, for beginners, we can think that "pointer" is "address" and "address" is "pointer", which is inaccurate but practical.
Now let's look at a more complex example:
Int * p1 = & total_count;
Int count2 = * (& p1 ));
Please tell me the count2 value within 10 seconds. Haha, okay. Let's make a little analysis.
Start from the bottom of the brackets:
P1 is a pointer whose value is the address of total_count;
& P1 is an address, that is, the address of the pointer p1. Let's take a look at the tips just now, that is, the address p1 address, that is, the pointer of the pointer p1.
* (& P1) is a value. This value is the memory value of the address indicated by & p1, which is actually the value of p1. & A reference operation is performed on p1, * Re-referencing it does not actually change. The value of * (& p1) is the address of total_count.
* (& P1) is the value of total_count, that is, 542.
At this point, the analysis of this expression ends, but in actual programming, no one will use such a complex expression for encoding.
4. pointer
Pointer, that is, the address of the address. The first address is an address in a narrow sense. The address is actually "valued", and the second address is an address in the traditional sense. This sentence is difficult to understand. Let's take a look at the following expression;
Int * p1 = & total_count;
Int ** p2 = & p1;
The meaning of the first expression is clear. We have applied for a pointer variable, or an address variable, which stores the total_count address.
The second expression is analyzed step by step. p1 is a pointer, that is, an address, and an ampersand. In addition, p1 is the address, or the pointer of a pointer. For int ** p2, we can understand int * X in this way. We define a pointer variable X, which is * p2, that is, X is also a pointer variable, in addition, we applied for a p2 pointer variable, which also points to a pointer variable.
Similarly, int *** p3 = & p2;
Int *** p4 = & p3;
All expressions are valid. However, in actual encoding, the most common situations are pointer and pointer.
From Kernel & UI