Pointers in C Language
First, let's look at the meanings of the following types.
1) int p;
This is a common integer variable.
2) int * p;
Starting from p, it is first combined with *, indicating that p is a pointer, and then combined with int, indicating that the type of content pointed to by the pointer is int type. So p is a pointer to integer data.
3) int p [3];
Starting from p, it is first combined with [], indicating that p is an array and then combined with int, indicating that the elements in the array are integer, therefore, p is an array composed of integer data.
4) int * p [3];
Starting from p, p is first combined with []. Because [] has a higher priority than *, p is an array. Then combined with *, the elements in the array are pointer types. Then combined with int, it means that the type of the content pointed to by the pointer is integer, so p is an array composed of pointers pointing to integer data.
5) int (* p) [3];
Starting from p, it is first combined with * Because () has the highest priority, which means p is a pointer (this step can be ignored with "", just to change the priority ). Then it is combined with [], indicating that the pointer points to an array and then combined with int, indicating that the elements in the array are integer. Therefore, p is a pointer to an array composed of integer data.
6) int ** p;
Starting with p, p is first combined with *, indicating that p is a pointer, and then combined with *, indicating that the element pointed to by the pointer is still a pointer, and then combined with int, this double Pointer Points to an integer. Therefore, p is a second-level pointer to the integer data. Since second-level pointers and more advanced pointers are rarely used in complex types, we will not consider multi-level pointers for more complex types. We will only consider first-level pointers at most.
7) int p (int );
Starting from p, it is first combined with (), indicating that p is a function, and then goes to () for analysis, indicating that this function has an integer variable parameter, then it is combined with an int, indicating that the return value of the function is an integer data.
8) int * p (int );
Starting from p, it is first combined with (), indicating that p is a function, and then enters () for analysis, indicating that this function has an integer variable parameter, then it is combined with the external *, indicating that the return value of the function is a pointer, and then combined with the int, indicating that the returned value pointer points to the int type.
9) int (* p) (int );
Starting from p, it is first combined with *, indicating that p is a pointer, and then combined with (), indicating that the pointer points to a function, and then enters () for analysis, it indicates that the function has an int-type parameter, which is combined with the int at the outermost layer, indicating that the return type of the function is an integer, therefore, p is a pointer to a function with an integer parameter and return an integer type.
10) int * (* p (int) [3];
You can skip this step and ignore this type. It is too complicated. Starting from p, it first combines with (), indicating that p is a function, and then goes to () for analysis, indicating that the function has an int type parameter. Then it is combined with the outer *, indicating that the function returns a pointer. Then, it goes to the outermost layer and combines it with [], indicating that the returned Pointer Points to an array, and then is combined with *, indicating that the elements in the array are pointers, and then combined with int, the Pointer Points to integer data. Therefore, p is a function that refers to an integer and returns a pointer variable pointing to an array composed of integer pointer variables.
After understanding these types, other types are also small dishes, but we generally do not use too complex types, which will greatly reduce the readability of the program. Please use them with caution, the above types are enough for us to use.
1. Understand pointers
A pointer is a special variable. The value stored in it is interpreted as an address in the memory. To understand a pointer, we need to understand the four aspects of the pointer:
1) pointer type
2) Type pointed to by pointer
3) pointer value or memory zone pointed to by pointer
4) memory occupied by pointers
1.1 pointer type
From the syntax perspective, you only need to remove the pointer name in the pointer declaration statement, and the rest is the pointer type. This is the type of the pointer.
(1) int * ptr; // the pointer type is int * (2) char * ptr; // the pointer type is char * (3) int ** ptr; // The pointer type is int ** (4) int (* ptr) [3]; // the pointer type is int (*) [3] (5) int * (* ptr) [4]; // the pointer type is int * (*) [4]
1.2 type pointed to by pointer
When you access the memory area pointed to by the pointer, the type pointed to by the pointer determines what the compiler will regard the content in the memory area. In terms of syntax, you only need to remove the pointer name and the pointer declarative * on the left of the name in the pointer declaration statement, and the rest is the type pointed to by the pointer.
(1) int * ptr; // The Pointer Points to an int (2) char * ptr; // The Pointer Points to a char (3) int ** ptr; // The Pointer Points to the int * (4) int (* ptr) [3]; // The Pointer Points to the int () [3] (5) type) int * (* ptr) [4]; // The type pointed to by the pointer is int * () [4]
The pointer type (the pointer type) and the pointer type are two concepts.When you get familiar with C, you will find that, the concept of "type", which is mixed with pointers, is divided into two concepts: "pointer type" and "Type pointed to by Pointers". It is one of the key points of proficient pointers.
1.3 pointer value or memory zone or address pointed to by pointer
The pointer value is the value stored by the pointer itself. This value will be treated as an address by the compiler rather than a general value. In a 32-bit program, the value of all types of pointers is a 32-bit integer, because the 32-bit program's memory address is all 32-bit long. The memory area pointed to by the pointer starts from the memory address represented by the pointer value, and the length is a memory area of sizeof (type pointed to by the pointer. Later, we will say that the value of a pointer is XX, which means that the pointer points to a memory area with XX as the first address. We will say that a pointer points to a memory area, it is equivalent to saying that the pointer value is the first address of the memory area.
The memory zone pointed to by the pointer and the type pointed to by the pointer are two completely different concepts. For example"Int * ptr;", The type pointed to by the pointer already exists, but since the pointer has not been initialized, the memory zone it points to does not exist, or it is meaningless.
1.4 The Memory occupied by the pointer itself
How much memory does the pointer occupy? You just need to use the sizeof function (pointer type) to test it. On a 32-bit platform, the pointer occupies 4 bytes. The memory occupied by pointers can be used to determine whether a pointer expression is left.
The left value is simply an expression that can be placed on the left of the value assignment operator. Let's take a look at his definition: if an expression can be referenced to an object, and this object is a piece of memory space that can be checked and stored, then this expression can be used as a left value. Of course, with the left value, there will be the right value concept: the right value refers to reference a data stored in a memory address. A variable can be both the left value and the right value. The two variables are not opposite.
2. pointer operation
The pointer can be added or subtracted from an integer. The meaning of this operation of pointer is different from that of addition and subtraction of common values, in units:
Char a [20]; int * ptr = (int *) a; // forced type conversion does not change the type ptr ++ OF;
In the preceding example, the pointer ptr is of the int type * and it points to the int type. It is initialized to the integer variable. In the next 3rd sentences, the pointer ptr is added with 1, and the compiler processes it like this: it adds the value of the pointer ptr with sizeof (int), in a 32-bit program, 4 is added because int occupies 4 bytes in 32-bit programs. Because the address is in bytes, the address pointed by ptr is increased by four bytes from the address of the original variable a to the high address. Because the length of the char type is one byte, the ptr originally points to the four bytes starting from Unit 0th of array, this point points to the four bytes starting from Unit 4th in array.
We can use a pointer and a loop to traverse an array:
int array[20]={0}; int *ptr=array; for(i=0;i<20;i++) { (*ptr)++; ptr++; }
In this example, the value of each unit in the integer array is added to 1. Since every loop adds a pointer ptr to one unit, each loop can access the next unit of the array.
Let's look at the example:
char a[20]="You_are_a_girl";int *ptr=(int *)a;ptr+=5;
In this example, ptr is added with 5, and the compiler processes it like this: add the value of the pointer ptr to the value of the 5-bysizeof (int ), in the 32-bit program, 5 is multiplied by 4 = 20. Because the address unit is byte, the current ptr points to the address, compared to the address pointed by the ptr after the addition of 5, move 20 bytes to the high address. In this example, if the ptr before 5 is not added, it points to the four bytes starting with unit 0th of array a. After 5 is added, the ptr points out of the valid range of array. Although this situation may cause problems in applications, it is possible in terms of syntax. This also reflects the flexibility of pointers. If in the above example, ptr is subtracted from 5, the processing process is similar, except that the ptr value is subtracted from 5 by sizeof (int ), the new ptr points to an address that moves 20 bytes to the lower address direction than the original ptr.
Here is another example:
#include<stdio.h>int main(){ char a[20]="You_are_a_girl"; char *p=a; char **ptr=&p; //printf("p=%d\n",p); //printf("ptr=%d\n",ptr); //printf("*ptr=%d\n",*ptr); printf("**ptr=%c\n",**ptr); ptr++; //printf("ptr=%d\n",ptr); //printf("*ptr=%d\n",*ptr); printf("**ptr=%c\n",**ptr);}
Misunderstanding 1The output answer is Y and o. Misunderstanding: ptr is a second-level pointer of char. When "ptr ++;" is executed, it will add a sizeof (char) to the pointer ), therefore, if the above results are output, this may be the result of a small number of people.
Misunderstanding 2The output answer is Y and a. Misunderstanding: ptr points to a char * type. When "ptr ++;" is executed, it will add a sizeof (char *) to the pointer *) (Some people may think that this value is 1, so they will get the answer from misunderstanding 1. This value should be 4, refer to the previous content), that is, & p + 4. Isn't a single value operation pointing to the fifth element in the array? Isn't the output result The Fifth Element in the array? The answer is no.
Positive Solution:The ptr type is char ** and the pointer type is char *. The pointer address is p (& p). When "ptr ++;" is executed, it will add a sizeof (char *) to the pointer, that is, & p + 4. Where does * (& p + 4) point to? Ask God, or he will tell you where it is? Therefore, the final output is a random value, which may be an invalid operation ..
Summary:After a pointer ptrold is added (subtracted) by an integer n, the result is a new pointer ptrnew. The type of ptrnew is the same as that of ptrold, ptrnew points to the same type as ptrold. The value of ptrnew will increase (decrease) n times of sizeof (type pointed to by ptrold) bytes than the value of ptrold. That is to say, ptrnew will point to a memory area that moves n x sizeof (the type indicated by ptrold) bytes to the high (low) Address direction than the memory area pointed to by ptrold.
Addition and subtraction of pointers: The two pointers cannot be used for addition operations. This operation is invalid because after addition, the result is directed to an unknown place and meaningless. The two pointers can be used for subtraction, but must be of the same type. They are generally used in arrays.
3. pointer expression
If the result of an expression is a pointer, this expression is called a pointer expression. Below are some examples of pointer expressions:
Int a, B; int array [10]; int * pa; int * pb; int ** ptr; pa = &; // & a is a pointer expression ptr = & pa; // & pa is also a pointer expression * ptr = & B; // * ptr and & B are both pointer expressions pa = array; pa ++; // This is also the pointer expression pb = * ptr; // * parr is the pointer expression pb = * (ptr + 1); // * (ptr + 1) is the pointer expression
Since the result of a pointer expression is a pointer, the pointer expression also has four elements of the pointer: the pointer type, the type pointed to by the pointer, and the memory area pointed to by the pointer, memory occupied by the pointer itself. When the result pointer of a pointer expression has clearly occupied the memory of the pointer itself, the pointer expression is a left value, otherwise it is not a left value.
In the above example, & a is not a left value, because it does not occupy clear memory. * Ptr is a left value, because * ptr pointer occupies the memory. In fact, * ptr is the pointer pa. Since pa already has its own location in the memory, * ptr certainly has its own position.
4. pointerAndArray
The array name can be regarded as a pointer. See the following example:
Int array [10] = {,}; int value; value = array [0]; // It can also be written as: value = * array; value = array [3]; // It can also be written as: value = * (array + 3); value = array [4]; // It can also be written: value = * (array + 4 );
In the above example, the array name array represents the array itself, and the type is int [10]. However, if we regard array as a pointer, it points to the 0th units of the array, and the type is int *, the Type pointed to is the type of the array unit, that is, int. Therefore, it is not surprising that * array is equal to 0. Similarly, array + 3 is a pointer to the array's 3rd units, so * (array + 3) is equal to 3, and so on.
Char * str [3] = {"Hello, thisasample! "," Hi, goodmorning. "," Helloworld "}; char s [80]; strcpy (s, str [0]); // It can also be written as strcpy (s, * str); strcpy (s, str [1]); // It can also be written as strcpy (s, * (str + 1); strcpy (s, str [2]); // It can also be written as strcpy (s, * (str + 2 ));
In the preceding example, str is a three-unit array, and each unit of the array is a pointer, each pointing to a string. If the pointer array name str is used as a pointer, it points to the cell 0th of the array. Its type is char **, and it points to char *. * Str is also a pointer. Its type is char *. It points to char and its address is the string "Hello, thisasample! ", That is, the address of 'H.
Note: A string is equivalent to an array. It is stored as an array in the memory, but the string is an array constant and the content cannot be changed. It can only be the right value. If it is regarded as a pointer, it is a constant pointer and a pointer constant.
Constant pointer:The pointer is a constant and cannot be changed. It cannot be changed after it points to an address, but the content it points to can be changed and can be easily mixed with the pointer constant. Therefore, when readingConstant pointLiterally, a constant points to an address.
Pointer constant:It indicates that the Pointer Points to the content that cannot be changed, but its pointer itself is a variable that can be changed to the content that is readPoint to constantLiterally, it points to a constant.
Str + 1 is also a pointer pointing to unit 1st of the array. Its type is char **, and its type is char *. * (Str + 1) is also a pointer. Its type is char * and it points to char, which points to "Hi, goodmorning. the first character 'H '.
The following is a summary of the array name (which is also stored in the array:
If an array TYPE array [n] is declared, the array name array has two meanings: First, it represents the entire array, and its TYPE is TYPE [n]; second, it is a constant pointer. the pointer TYPE is TYPE *, and the Pointer Points to TYPE, that is, the TYPE of the array unit, the Pointer Points to the memory zone, which is the unit 0th of the array. the pointer occupies a separate memory zone. Note that it is different from the memory zone occupied by the array unit 0th. The value of this pointer cannot be modified, that is, the expression similar to array ++ is incorrect.
In different expressions, array names can play different roles. In the expression sizeof (array), the array name array represents the array itself. Therefore, the sizeof function measures the size of the entire array. In the expression * array, array acts as a pointer. Therefore, the result of this expression is the value of unit 0th of the array. Sizeof (* array) measures the size of the array unit. Expression array + n (where n = 0, 1, 2 ,.....) in, array is a pointer, so the result of array + n is a pointer, its TYPE is TYPE *, it points to TYPE, it points to the array number n. Therefore, sizeof (array + n) measures the pointer size. In the 32-bit program, the result is 4.
int array[10];int (*ptr)[10];ptr=&array;
In the above example, ptr is a pointer and its type is int (*) [10]. It points to int [10]. We use the first address of the entire array to initialize it. In the statement "ptr = & array", array represents the array itself.
5. pointer and struct
You can declare a pointer to a structure object.
Struct MyStruct {int a; int B; int c ;}; struct MyStruct ss ={20, 30, 40}; // declares the structure object ss, initialize the ss members to 20, 30, and 40. Struct MyStruct * ptr = & ss; // declare a pointer to the structure object ss. Its type is MyStruct *, and it points to MyStruct. Int * pstr = (int *) & ss; // declare a pointer to the structure object ss. However, pstr is different from the type ptr to which it is directed.
1) How can I access the three member variables of ss through the pointer ptr?
Answer:
Ptr-> a; // point to the operator, or you can write (* ptr). a in this way. We recommend that you use the former.
Ptr-> B;
Ptr-> c;
2) How can I access the three member variables of ss through the pointer pstr?
Answer:
* Pstr; // access a, a member of the ss.
* (Pstr + 1); // access B, a member of ss.
* (Pstr + 2); // access the ss member c.
You know, using pstr to access structure members is not formal. That is, the struct cannot be strongly converted to pointer access. When each member of a structure object is stored, in a compiling environment, it may require word alignment, double-word alignment, or other alignment, you need to add several "fill bytes" between two adjacent members, which may lead to several gaps between each member. "* (Pstr + 1);" this gap may have been accessed.
6. pointers and functions
1) A pointer can be declared as a pointer to a function.
Int fun1 (char *, int); int (* pfun1) (char *, int); pfun1 = fun1; int a = (* pfun1) ("abcdefg", 7 ); // call a function through a function pointer.
2) the pointer can be used as the form parameter of the function. In a function call statement, you can use a pointer expression as a real parameter.
int fun(char *);int a;char str[]="abcdefghijklmn";a=fun(str);int fun(char *s){ int num=0; for(int i=0;;) { num+=*s;
s++; } return num;}
In this example, function fun counts the sum of the ASCII values of each character in a string. As mentioned above, the array name is also a pointer. In function calls, after str is passed to the form parameter s as a real parameter, the str value is actually passed to s, s points to the same address as str,Str and s respectively occupy their respective storage space. In the function body, the auto-increment 1 operation on s does not mean that the str auto-increment 1 operation is performed at the same time.
7. pointer type conversion
When we initialize a pointer or assign a value to a pointer, the left side of the value pair is a pointer, and the right side of the value pair is a pointer expression. In most cases, the pointer type is the same as that of the pointer expression, and the pointer type is the same as that of the pointer expression.
float f=12.3;float *fptr=&f;int *p;
In the above example, what should we do if we want the pointer p to point to the real number f? "P = & f ;"? No. Because the pointer p is of the int type *, it points to the int type. The result of Expression & f is a pointer. the pointer type is float *, and the pointer type is float. The two are inconsistent. The direct assignment method is not feasible. For our purpose, forced type conversion is required: "p = (int *) & f ;".
If there is a pointer p, we need to change its TYPE and the TYPE to TYEP * and TYPE, then the syntax format is: "(TYPE *) p ;". In this way, the forced TYPE conversion result is a new pointer. the TYPE of the new pointer is TYPE *, which points to the TYPE and the address pointed to by the original pointer. All properties of the original pointer p are not modified (remember ).
If a function uses a pointer as a form parameter, the type must be consistent during the combination of the real parameters and form parameters in the function call statement. Otherwise, a forced conversion is required.
void fun(char*);int a=125,b;fun((char*)&a);void fun(char* s){ char c; c=*(s+3); *(s+3)=*(s+0); *(s+0)=c; c=*(s+2); *(s+2)=*(s+1); *(s+1)=c;}
Note that this is a 32-bit program, so the int type occupies four bytes, And the char type occupies one byte. Function fun is used to reverse the order of four bytes of an integer. Have you noticed? In a function call statement, the result of the real parameter & a is a pointer. Its type is int *, and its type is int. The pointer type of the parameter is char *, which points to char. In this way, in the combination of real parameters and form parameters, we must perform a conversion from the int * type to the char * type.
Using this example, we can imagine the conversion process of the compiler: the compiler first constructs a temporary pointer char * temp, and then runs temp = (char *) &, finally, pass the temp value to s. So the final result is: the type of s is char *, which points to char, and the address it points to is the first address of.
We already know that the pointer value is the address pointed to by the pointer. In a 32-bit program, the pointer value is actually a 32-bit integer. Can an integer be directly assigned to the pointer as the pointer value? Like the following statement:
Unsigned int a; TYPE * ptr; // TYPE is int, char, or structure TYPE. a = N; // N must represent a valid address ptr = (TYPE *) a;
Strictly speaking, the TYPE * here is different from the TYPE * in the pointer TYPE conversion. Here (TYPE *) means to treat the value of the unsigned integer a as an address. It is emphasized that the value of a must represent a valid address. Otherwise, an invalid operation error occurs when you use ptr.
Think about whether or not we can take the address pointed to by the pointer as an integer. The following example shows how to take the value of a pointer as an integer and then assign this Integer as an address to a pointer:
Int a = 123, B; int * ptr = & a; char * str; B = (int) ptr; // use the pointer ptr value as an integer to get str = (char *) B; // assign the value of this Integer as an address to the pointer str
8. pointer security issues
char s='a';int *ptr; ptr=(int *)&s;*ptr=1298;
In the above example, the pointer ptr is an int * type pointer, which points to an int type. It points to the first address of s. In 32-bit programs, s occupies one byte, And the int type occupies four bytes. The last statement not only changes the byte occupied by s, but also changes the three bytes in the high address direction adjacent to s. What are these three bytes? Only the compiled program knows, but the program writer is unlikely to know. Maybe these three bytes store very important data, maybe these three bytes are exactly a piece of code of the program, and because of your sloppy application of pointers, the value of these three bytes has been changed! This will cause a collapse error.
char a;int *ptr=&a;ptr++;*ptr=115;
The preceding example can be compiled and executed. But do you see? After we perform the auto-increment 1 operation on the pointer ptr, the ptr points to a storage area adjacent to the high address of the shaping variable. What is in this bucket? We don't know. It may be a very important piece of data, or even a piece of code. The first sentence is to write a piece of data into this bucket! This is a serious error. So when using pointers, the programmer must be very clear about where my pointers actually point. When using pointers to access arrays, be sure not to go beyond the low-end and high-end limits of arrays. Otherwise, similar errors may occur.
In the forced TYPE conversion of the pointer: ptr1 = (TYPE *) ptr2. If sizeof (ptr2 TYPE) is greater than sizeof (ptr1 TYPE ), therefore, it is safe to use the pointer ptr1 to access the storage zone pointed to by ptr2. If sizeof (ptr2 type) is smaller than sizeof (ptr1 type), it is insecure when ptr1 is used to access the bucket to which ptr2 points.