catalogue
I What is the pointer
Understand pointer
1. Pointer is the number of the smallest unit in memory, that is, the address
2. The pointer in spoken English usually refers to the pointer variable, which is used to store the memory address (i.e. store the pointer)
Conclusion: pointer is the address. In colloquial language, pointer usually refers to pointer variable.
Pointer variable
1. We can take out the initial memory address of the variable through & (take address operator) and store the address in a variable, which is a pointer variable.
2. Pointer variable, which is used to store the variable of address. (the values stored in the pointer are treated as addresses)
How big is a small unit? (1 byte)
Pointer addressing
For a 32-bit machine, it is assumed that there are 32 address lines. It is assumed that each address line generates high level (high voltage) and low level (low voltage) during addressing, which is (1 or 0)
Then the address generated by 32 address lines will be:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001 ...
11111111 11111111 11111111 11111111
There are 32 addresses to the power of 2.
Each address identifies a byte, so we can address 4G of free memory (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB)
Here we understand:
1. On a 32-bit machine, the address is a binary sequence of 32 zeros or 1s, and the address must be stored in a space of 4 bytes, so the size of a pointer variable should be 4 bytes.
2. On a 64 bit machine, if there are 64 address lines, the size of a pointer variable is 8 bytes to store an address.
Summary: pointer variables are used to store addresses, which uniquely mark an address space. The size of the pointer is 4 bytes on 32-bit platforms and 8 bytes on 64 bit platforms.
II Pointer and pointer type
1. The definition method of pointer is: type +*
2. Meaning of pointer type
① The pointer type determines how far the pointer can go in one step, that is, the step size
② The pointer type determines the permission of pointer dereference
Example:
#include <stdio.h> //Demonstration example int main() { int n = 10; char *pc = (char*)&n;//Take out the address of n and store it in the pointer variable pc of char type int *pi = &n;//Take out the address of n and store it in the pointer variable pi of type int printf("%p\n", &n); printf("%p\n", pc); printf("%p\n", pc+1); printf("%p\n", pi); printf("%p\n", pi+1); return 0; }
Program running results
The running results of the program are shown in the figure above:
We found that the printed address of pc+1 is 1 more than that of pc, that is, 1 more byte, that is, one more char type. Compared with pi, pi+1 has 4 more bytes, that is, an int type. The extra part is the same size as their pointer type, so it can be concluded that the pointer type determines how far the pointer can go, that is, the step length
Example 2:
//Demonstration example #include <stdio.h> int main() { int n = 0x11223344; char *pc = (char *)&n;//Store n in char type pointer variable int *pi = &n;//Store n in the pointer variable pi of type int *pc = 0; //Dereference *pi = 0; //Focus on observing their changes in memory during debugging. return 0; }
The memory changes are shown in the figure above. We obviously observed that when * pc=0, only one byte of memory is 0, while * pi=0 actually makes four bytes of memory 0.
So it is concluded that the pointer type determines the permission of pointer dereference.
For an integer array, use the integer pointer for the operation of one element per element. If you want the operation of one byte per byte, use the char pointer
3. Field pointer
Concept: Wild pointer means that the position pointed by the pointer is unknowable (random, incorrect and unrestricted), just like a wild dog
Origin of wild pointer
1. Pointer not initialized
int main() { //Here p is called the wild pointer int *p;//p is a local pointer variable. If the local variable is not initialized, it defaults to a random value! *p = 10;//Illegal access to memory return 0; }
2. Pointer cross-border access
#include <stdio.h> int main() { int arr[10] = {0}; int *p = arr; int i = 0; for(i=0; i<=11; i++) { //Since arr has only 10 elements, arr[11] is out of the range of the array. When the range pointed to by the pointer exceeds the range of array arr, p is the wild pointer *(p++) = i; } return 0; }
3. The space pointed by the pointer is released
int *test() { int a=10; return &a; } int main() { int *p=test();//(illegal access to memory), because after the function is called, a is destroyed, but the address is still retained, so it is illegal. *p=20; return 0; }
How to avoid wild pointer
1. Initialize pointer
2. Be careful that the pointer is out of range
3. Place NULL in time after the pointer points to the space
4. Check the validity of the pointer before use
5. Avoid returning the address of local variables
Take chestnuts for example:
#include <stdio.h> int main() { //When you don't know what address p should initialize at present, it is directly initialized to a null pointer int *p=NULL; //Clearly know the initialization value int a=10; int *p=&a; //The C language itself does not check for data cross-border behavior if(p!=NULL)//Check whether the pointer is empty *p=0; }
4. Pointer operation
Pointer + - integer
The addition and subtraction of pointer variables is to add and subtract the stored address.
#define N_VALUES 5 float values[N_VALUES]; float *vp; for (vp = &values[0]; vp < &values[N_VALUES];)//Pointer relational operation, that is, comparing the size of the address. { *vp++ = 0;//Pointer + - integer; }
Pointer pointer
1. Pointer - the pointer gets the number of elements between two pointers
2. The premise of pointer subtraction is that two pointers point to the same space
Example 1:
int main() { int arr[10]={1,2,3,4,5,6,7,8,9,10}; printf("%d\n",&arr[9]-&arr[0]);//The result is 9, including arr[0], but excluding arr[9] char c[5]; printf("%d\n",&arr[9]-&c[0]);//err, because it's not in the same space return 0; }
Example 2: method of finding string length
//Counter int my_strlen1(char* str) { int count = 0; while (*str != '\0') { count++; str++; } return count; } //recursion int my_strlen2(char* pa) { if (*pa == '\0') { return 0; } else return 1 + my_strlen2(pa + 1); } //Pointer pointer. As long as you get the address of the first element and the address of \ 0, you can get the number of elements between the two pointers. int my_strlen3(char* pa) { char* start = pa; while (*pa != '\0') { pa++; } return pa - start;//Pointer - the pointer gets the number of elements between } #include <stdio.h> int main() { //strlen();- Find string length int len1 = my_strlen1("abc"); int len2 = my_strlen2("abc"); int len3 = my_strlen3("abc"); printf("%d\n", len1); printf("%d\n", len2); printf("%d\n", len3); return 0; }
Pointer + pointer meaningless
Relational operation of pointer
#define N_VALUES 3 for(vp = &values[N_VALUES]; vp > &values[0];)//The comparison between vp and & values [0] here is actually a relational operation { *--vp = 0; }
Its operation steps are value [3] > values [0] → values[2]=0 → judgment → values[1]=0 → judgment → values[0]=0 → judgment → stop
We rewrite the code:
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--) { *vp = 0; }
Its operation steps are values [2] > = & value [0] → values[2]=0 → judgment → values[1]=0 → judgment → values[0]=0 → judgment value [- 1] > = & values [0] → stop execution
Although the second method seems easier to understand and can successfully complete the task on most compilers, we should avoid it because the C language standard does not guarantee its feasibility.
Standard provisions:
A pointer to an array element is allowed to be compared with a pointer to the memory location after the last element of the array, but not with a pointer to the memory location before the first element. It can be remembered that the pointer of the array element is allowed to compare with arr[max+1], but the pointer of the array element is not allowed to compare with arr[-1]
5. Pointer and array
Prove that p+i produces the address with subscript i
int main() { int arr[10]={0}; int *p=arr; int i=0; for(i=0;i<10;i++) { printf("%p<==>%p",&arr[i],p+i); } return 0; }
As like as two peas, the address of &arr[i] and p+i is identical, so it can be determined that the address generated by p+i is the address of the array with subscript i, that is, p+i=arr[i].
*Extension:
int main() { int arr[10]={0}; int *p=arr; return 0; }
Arr [2] < = = > * (P + 2) < = > * (2 + P) / / commutative law
Because both p and arr are the first element addresses, they can also be replaced
arr[2]<==>*(arr+2)<==>*(2+arr)
arr[2]<==>p[2]<==>*(p+2)
We know that [] is an operator and 2 and arr are two operands, so we can exchange positions
arr[2]–>*(arr+2)–>*(2+arr)–>2[arr]
6. Secondary pointer
int main() { int a=10; int *pa=&a;//pa is a pointer variable int* *ppa=&pa//*ppa first indicates that pa is a pointer, and then int * indicates that the type of pa is int *. At this time, ppa is a secondary pointer variable //Dereference - * ppa==pa //*pa==a //* *ppa==a int ** *ppa=&ppa;//Three level pointer, rarely used return 0; }
7. Pointer array
Shaping array: store shaping
Character array: store characters
Pointer array – an array of pointers
For example: int * Parr [5]// Int * indicates that the type of this array is an array of integer pointers
End of this chapter. If you want to have a deeper understanding of pointers, you can see the later advanced pointers