This is the 7th day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021
Pointer to the
This Pointer goes a little further than the Pointer in C, but it’s not all there is to it, as we’ll see later in the advanced section.
A pointer to define
Memory division
Memory is a large space consisting of small one-byte memory units, each of which is bound to an address, that is, the number of the memory unit. Like an ID number, an address uniquely identifies a memory location. Such as:
Pointers and pointer variables
The address points directly to another value stored in memory. Since the desired variable unit can be found through the address, the address points to a uniquely determined memory unit, so the address is visualized as a pointer.
We now define an integer variable a and allocate 4 bytes to it in memory. From this we can see that the essence of defining variables is to allocate space in memory. The address of the first byte of variable A is 0x0012FF40, which represents the address of variable A.
So what is a pointer variable? Now let’s define a “pointer” to a variable a.
We use &a to take out the address of variable A and put it in variable pa. Since pa holds the address, we use int * to define variable PA.
int * pa = &a;
Copy the code
So pa is also a real variable in memory that stores the address number. Such variables are called pointer variables.
conclusion
- Pointers are addresses, and addresses are Pointers.
- Pointer variables are variables that hold addresses, and their contents are treated as addresses.
Pointer variables are often referred to simply as Pointers, and we need to distinguish between Pointers and pointer variables in context.
Pointer to the size
- How big is a memory cell?
- How are addresses numbered?
First, let’s analyze why the size of a memory cell is a byte.
For 32-bit machines, that is, 32 address lines, the electrical signals (positive/negative) generated by each address line at the time of addressing are converted into digital signals, with positive points being 1 and negative points being 0. In more general terms, it’s one if it’s powered on, and zero if it’s not.
So how many 01 combinations are there in 32 root address lines? High school permutation knowledge can show that there are 2322^{32}232 01 sequences. That’s going from 32 all zeros to 32 all ones.
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
… …
11111111 11111111 11111111 11111110
11111111 11111111 11111111 11111111
64-bit machines, of course, have 2642^{64}264 permutations.
Since we have 2322^{32}232 permutations on our 32-bit machine.
Each binary sequence is the serial number of a memory unit, then 2322 ^ {32} 232 memory units available, converted to decimal number is 4294967296 4294967296 4294967296.
If each memory cell is 1 bit size, then divide by 8, 536870912, 536870912, 536870912 byte, there were 524288, 524288, 524288 KB, Divide by 1024 and you get the familiar 512512512 MB, which is about half a GB. Isn’t it wasteful that a char variable needs 8 addresses?
If each memory cell is 1 byte, this translates to exactly 4 GB. That’s just fine. In the early days, we had one or two GIGABytes.
Pointer variables are used to store addresses. An address is 32 bits, so exactly four bytes are needed. So no matter what type, the size of the pointer variable is 4 bytes.
Of course, a 32-bit machine pointer is 4 bytes in size, and a 64-bit machine pointer is 8 bytes in size.
Pointer types
int a = 10;
int * pa = &a;
Copy the code
*
On behalf ofpa
Is a pointerint
On behalf ofpa
The variable pointed to is of typeint
Variables have different types, and obviously pointer variables have different types. The size of a pointer variable is 4 bytes on a 32-bit platform, regardless of its type. It is reflected in two aspects, one is pointer dereference, and the other is pointer addition or subtraction of integers.
Pointers dereference aspects
int a = 0x11223344;
int* pa = &a;
*pa = 0;
Copy the code
We create a variable a and point to it with a pointer pa, then dereference pa and set a to 0.
So let’s change the type of the pointer variable to int * pa and change it to char * pa.
The difference is that the pointer to int* accesses and modifies four bytes, while the pointer to char* modifies only one byte.
Pointer plus or minus positive
Now let’s add different types of Pointers to the same variable and add one to it. Such as:
You can see that the pointer to int* +1 skips backwards by 4 bytes and the pointer to char* +1 skips backwards by 1 byte.
conclusion
The pointer type determines:
- The number of bytes (memory size) that can be accessed during pointer dereference operations.
- Pointer to the
Plus or minus
An integer can skip several bytes (steps).
This way, we can skip different bytes with different types of Pointers and access the contents of the variable more carefully. Such as:
int arr[10] = { 0 };
/ / 1.
int* pa = arr;
/ / 2.
char * pa = arr;
for (int i = 0; i < 10; i++)
{
*(pa + i) = 1;
}
Copy the code
Two different Pointers bring different effects, as shown in the figure:
The first is to access the array elements one by one, and the second is to access the array character by character. Such as:
Wild pointer
Wild pointer definition
A pointer to an undefined position (random, incorrect, unbounded) is a wild pointer.
Incorrect location: indicates unallocated memory space, causing out-of-bounds access.
Origin of wild pointer
-
Pointer is not initialized
int* p;// Not initialized *p = 20; Copy the code
-
Pointer access out of bounds
int arr[10] = { 0 }; int* p = arr; for (int i = 0; i <= 10; i++)// Cross border visit { *(p + i) = i; } Copy the code
Out of bounds ok, but not out of bounds access ^_^.
-
sample
int* test(a){ int a = 10; return &a; } int main(a){ int* p = test(); printf("%d\n", *p);// The wild pointer is out of bounds return 0; } Copy the code
Here a is defined in the test function and will be destroyed when it is out of scope, so printing *p is out of bounds.
But our program still finds 10. Why is that?
The reason is that the a variable has not been destroyed by the operating system after its space is reclaimed, and the compiler reserves it once. And the argument is passed before the call, so *p is replaced with 10 before you call printf again.
- Let’s modify it a little bit before printing
*p
Is called again before theprintf
Functions such as:int* test(a){ int a = 10; return &a; } int main(a){ int* p = test(); printf("hehe\n"); printf("%d\n", *p); return 0; } Copy the code
So this call to printf overwrites the space that was allocated to A, and it’s allocated to printf. The usage of the stack area is to press the stack (if you do not know, you can go to see the opening and destruction of the stack area space).
-
So if we print printf(“%d\n”, *p); *p = 20; Words such as:
int* test(a){ int a = 10; return &a; } int main(a){ int* p = test(); *p = 20;// Access illegal memory return 0; } Copy the code
The compiler detects that the space is illegal and reports an error.
- Pointer to space freed
As you can see from the above example, the pointer p points to the freed memory space previously occupied by the test function, which is also a very dangerous thing and is bound to be a wild pointer. The pointer to dynamically opened memory will also be free, which prevents it from becoming a wild pointer.
How to avoid wild Pointers
-
Clear pointer initialization to determine the pointer
int* p = &a; int* p =NULL;// NULL if you don't know where to point to Copy the code
-
Beware of the pointer crossing the line
-
Set to NULL immediately after a pointer to a space is released
-
Avoid functions returning local variable addresses
-
Checking pointer validity
Null Pointers are not dereferenced.
if(p ! =NULL){
*p=20;// check that the pointer is not null
}
Copy the code
Assert (p) determines whether the pointer p is null and returns an error message.
Pointer arithmetic
Of course, pointer dereference is also a pointer operation, but we will consider only three classes here, after all, pointer dereference is a basic operation.
A pointer that adds or subtracts integers is still a pointer, just as a date added to days is still a date. A pointer minus a pointer is the number of elements, just as a date minus a date is the number of days.
Pointer to the+
-
The integer
float values[N_VALUE];
float* vp = values;
for (vp = &values[0]; vp < &values[N_VALUE];)
{
*vp++=0;
}
Copy the code
The code above iterates over zeros by means of Pointers to arrays.
-
In the circulatory body, VP comes after ++. Although ++ has a higher priority than *, post-+ + is used before ++.
So it looks like the pointer is dereferenced and then ++.
-
The increment of a float pointer skips one float length and jumps to the next element.
The pointer ++ skips the length of a type, regardless of the type.
-
When vp points to an address after the last element in the array, the loop ends.
Although the address does not belong to the array, it is only used to judge the size of the condition (address has high and low), and does not access the content of the address, so it is not out of bounds access.
This example deals with operations on two Pointers: the pointer plus or minus integers, that is, the pointer ++. Pointer relation operation, pointer to each other than the size of the judgment condition.
A pointer plus an integer is a pointer that jumps back by an integer of type size in bytes.
As shown in the figure, pointer P-1 is the size by which pointer P jumps forward by one type.
Pointer addition or subtraction of an integer is when the pointer skips an integer byte of type size backwards or forwards.
Pointer to the-
Pointer to the
Pointers can be subtracted to represent the “gap” between two addresses. Can we add Pointers to Pointers? The sum of two addresses is meaningless.
int arr[] = { 1.2.3.4.5.6.7.8.9.10 };
printf("%d\n", &arr[9] - &arr[0]);
Copy the code
What’s the answer to this question? Is it 36 or 9?
The answer is 9. The syntax says pointer – pointer, which yields the number of elements (subtracting subscripts) between two addresses.
Of course, the number of elements between two addresses can also be interpreted as the size of the bytes divided by the type size.
What if I were to do it in a different array? Such as:
int arr[] = { 1.2.3.4.5.6.7.8.9.10 };
char ch[] = { '1'.'2'.'3' };
printf("%d\n", &arr[9] - &ch[0]);
Copy the code
The compiler does not report an error because there are no syntax errors. But the resulting number is the number of elements. Is that an int or a char? So the numbers don’t make any sense.
So we get the premise of pointer subtraction: two Pointers point to the same space, like an array.
-
Pointer to the
-
The premise of a pointer is that two Pointers point to the same space. -
Pointer to the
-
Pointer, the absolute value of the resulting number is the number of elements between two addresses.
Application: strlen function
int my_strlen(const char* s){
char* begin = s;// mark the beginning
while(*s++);//s = \0
return s - begin - 1;// pointer subtraction
}
Copy the code
Pointer relation operation
Take the pointer plus or minus integer code example and modify it slightly.
/ / 1.
for(vp = &values[N_VALUE]; vp > &values[0];) { *--vp =0;
}
Copy the code
If you think of the space behind an array, you can also think of the contents of an array as being indexed by an array, because arrays are contiguous in memory. Traversing backwards, first — then dereferencing, will not result in an out-of-bounds access to the array. Let’s modify it a little bit:
/ / 2.
for(vp = &values[N_VALUE- 1]; vp >= &values[0]; vp--){ *vp =0;
}
Copy the code
For the last iteration, the pointer points to an address in front of values[0].
But we’ll try to choose the first method because the C standard allows a pointer to an array to be compared to a location in memory after the last element of the array, but not before the first element. As shown in figure:
The reason is that the compiler may store information about the array in front of it, such as the number of elements in the array. This may affect the operation of the program.
Pointer is also an address, address is a number is a number, you can compare the size. The relational operation on Pointers is to compare sizes.
Pointers and arrays
What’s the difference between Pointers and arrays? What’s the connection?
-
An array is a collection of elements of the same type that are stored in contiguous space. The size of an array depends on the element type and number of elements.
-
Pointer store address, is a variable. The size of the pointer is fixed to 4 (32bit) / 8 (64bit).
int arr[10] = { 0 };
printf("%p\n", arr);//0x0012ff40
printf("%p\n", &arr[0]);//0x0012ff40
Copy the code
The array name is the address of the first element in the array.
Ps: In both cases, the array name represents the entire array. In both cases, the array name represents the address of the first element.
sizeof(arr)
&arr
The name of the array can be stored as an address in a pointer variable, and we can access the array through a pointer.
In fact, when arrays are used as function parameters, they are reduced to Pointers. An entire array cannot be passed. But that’s another story.
int arr[10] = { 1.2.3.4.5.6.7.8.9.10 };
int* p = arr;
for (int i = 0; i < sz; i++)
{
printf("&arr[%d] = %p <===> p+%d = %p\n", i, &arr[i], i, p + i);
}
Copy the code
In other words, p+ I is the address of the array arr subscript I, which is essentially the same thing.
The secondary pointer
As the name implies, the second level pointer is used to store the address of the first level pointer, through the second level pointer can also access the first level pointer.
First we create a variable, A, which holds 10, so it has type int and address 0x0012FF40.
It then takes the address of a and creates a new variable pa and stores & A in it, so it has type int* and address 0x004FFABC.
Finally, a new variable ppa is created to store &p, so it is of type int** (secondary pointer).
You can find P by the address of P in ppa, and you can also find A by the address of A in P.
Indicates the meaning of * in the type
* in the gray box indicates that the variable is a pointer variable.
- Level 1 pointer
p
In front ofint
saidp
Object to point toa
isint
Type. - The secondary pointer
pp
In front ofint*
saidpp
Object to point top
Is of typeint*
Type. - Level 3 pointer
ppp
In front ofint**
saidppp
Object to point topp
Is of typeint**
Type.
Multi-level pointer dereference operation
*p = 1;
* *pp = 2;
* * *ppp = 3;
Copy the code
As shown in the code above, we analyze level by level.
- Logarithmic pointer
p
Reference solution*p
To finda
. - Pair pointer
pp
Reference solution*pp
To findp
, and then dereference**pp
To finda
. - Pair third order pointer
ppp
Reference solution*ppp
findpp
, and then dereference**ppp
To findp
Unreferenced again***ppp
To finda
.
So you can see that there are as many Pointers as there are references.
Pointer to an array
Pointer array definition
Before we answer the question of what a pointer array is, let’s look at what an integer array is and what a character array is.
int arr[10] = {0};
// Array of integers - An array of integer variables
char ch[10] = {'0'};
// Character array - An array of character variables
Copy the code
By analogy with an integer array and a character array, a pointer array is an array of pointer variables.
The types int and char before the array name indicate that the array element is of type int or char. So the type name before the pointer array name is either int* or char*. Such as:
// Int pointer array
int* parr[10];
// Array of character Pointers
char* pch[5];
Copy the code
For an integer pointer array, each element is the address of an integer variable. For a character pointer array, each element is the address of a character variable. The size of a pointer array depends only on the number of elements in the array.
Pointer array usage
int arr[] = { 10.20.30 };
int* parr[5] = {NULL};
/ / input
for (int i = 0; i < 3; i++)
{
parr[i] = &arr[i];
}
/ / output 1.
for (int i = 0; i < 3 ; i++)
{
printf("%d ", *parr[i]);
}
2 / / output.
for (int i = 0; i < 3; i++)
{
printf("%d ", **(parr+i));
}
Copy the code
- Remember to either initialize or specify the size. Pointer arrays remember that the content is initialized to a null pointer.
- When iterating through the array of pointer elements, remember to dereference them. With an array of
+i
When iterating through a set of elements, you resolve two layers of references.
At present, the corresponding pointer array is understood to this level, and we will learn the progression of Pointers later.