C-Language pointer traps
Transferred from: http://blog.csdn.net/porscheyin/article/details/3461670
"C language Strange Bizarre, trap heavy, but has achieved great success!" The father of the--c language Dennis M.ritchie. This sentence of Master Ritchie embodies the flexibility and extensive use of C, but it also reveals that C is a language that should always pay attention to its behavior when applied. C's design philosophy is that: programmers using C should know what they're doing. Sometimes the program written in C will make some inexplicable mistakes, seemingly root is difficult to find, but carefully explore will find a lot of errors because of the concept of unclear. In these "traps" that we often fall in, the number of pointers around is the most. This will analyze some of the problems encountered when using pointers to avoid falling into such traps in the future.
1. Pointers and string constants
In the second-hand initialization of the pointer, it is mentioned that a string constant can be assigned to a character pointer. But have any friends ever wondered why this could be initialized? Before we answer this question, let's figure out what a string constant is. A string constant is a sequence of characters that is inside a pair of double quotation marks (which can be empty).
When a string constant appears in an expression, in addition to the following three cases:
1. As the operand of the & operator;
2. The operand as the sizeof operator;
3. As the initialization value of the character array
String constants are converted to an array of characters that are pointed to by a pointer. For example: char *CP = "ABCDEFG"; the above 3 conditions are not met, so "ABCDEFG" is converted to an array of characters without a name, the array is initialized by ABCDEFG and a null character '/0 ', and a pointer constant is given, which is the address of the first character, But these are all done by the compiler. It is now possible to explain the reason for initializing a character pointer with a string constant, and the value of a string constant is a pointer constant. So for the following statements, friends should not be confused:
printf ("%c\n", * "ABCDEFG");
printf ("%c\n", * ("ABCDEFG" + 1));
printf ("%c\n", "ABCDEFG" [5]);
* "ABCDEFG": The value of a string constant is a pointer constant, which points to the first character of the string, and the reference to it gets A;
* ("ABCDEFG" + 1): The pointer to the arithmetic operation, it points to the next character, and then to its dereference, get B;
"ABCDEFG" [5]: Since "ABCDEFG" is a pointer, then "ABCDEFG" [5] can be written as * ("ABCDEFG" + 5), so get f.
Recall the methods you learned to initialize an array: char ca[] = {' A ', ' B ', ' C ', ' d ', ' e ', ' f ', ' g ', '/0 '}; This method is too clumsy, so the standard provides a quick way to initialize the character array: char ca[] = "ABCD EFG "; This string constant satisfies the 3rd of the above: used to initialize a character array, so it is not converted to an array of characters pointed to by a pointer. It is simply a simple way to initialize a character array with a single character. Then compare the following two statements:
Char ca[] = "ABCDEFG";
Char *CP = "ABCDEFG";
They do not have the same meaning, the former is the element that initializes a character array, and the latter is a true string constant, as shown in:
Char ca[] = "ABCDEFG";
Figure 1
Char *CP = "ABCDEFG";
Figure 2
Note that the string constants used to initialize the character array, the compiler allocates space for the character array in the stack, and then copies all the characters in the string to the array, while the string constants used to initialize the character pointer are arranged by the compiler into a read-only data store, but are also stored in the form of a character array. 2. We can read a string constant through a character pointer, but we cannot modify it, or a run-time error occurs. as the following example:
1. Charca[] = "ABCDEFG";
2. CHAR*CP = "ABCDEFG";
3. Ca[0]= ' B ';
4. printf ("%s\n", CA);
5. Cp[0]= ' B ';
6. printf ("%s\n", CP);
The 3rd line of the program modifies a string constant that is not in the read-only data area but is copied from the string constant to an element of the character array CA that exists in the stack. However, a run-time error occurs when line 5th modifies the string constant in the read-only data area that is used to initialize the character pointer. Let's not assume that all string constants are stored in different addresses, and standard C allows the compiler to use the same storage address for two string constants that contain the same characters, and that's what most compilers in the real world do. Consider the following procedure:
charstr1[] = "ABC";
charstr2[] = "ABC";
CHAR*STR3 = "ABC";
CHAR*STR4 = "ABC";
printf ("%d\n", str1 = = str2);
printf ("%d\n", str3 = = STR4);
The result of the output is: 0 1
STR1,STR2 is an array of two different characters initialized to "ABC", each with their own space in the stack, while STR3,STR4 is a two-character pointer that is initialized to a string constant containing the same characters, pointing to the same area.
2. Strlen () and sizeof
Please see the following procedure:
Char a[1000];
printf ("%d\n", sizeof (a));
printf ("%d\n", strlen (a));
The output of this code may not necessarily be 1000, 0. The result of sizeof (a) must be 1000, but the results of strlen (a) cannot be determined. The root cause is that strlen () is a function, and sizeof is an operator, which results in a variety of them:
1. sizeof can use a type (enclosed in parentheses) or a variable to do the operand, whereas strlen () accepts only the char* character pointer as a parameter, and the string that the pointer points to must end with '/0 ';
2. sizeof is an operator, the array name using sizeof when the entire array is the size of the memory, and the array name as a parameter to strlen () after the name is converted to a pointer to the first element of the array;
3. The result of sizeof is determined at compile time, and strlen () is called at runtime.
Since the array a[1000] in the example above is not initialized, the number of elements and elements in the array is indeterminate, possibly random, so a different value is obtained with strlen (a), depending on the resulting random number, but the result of sizeof must be 1000. Because sizeof is a compile-time fetch in Char a[1000], both char and 1000 are the two information to compute space.
3. The const pointer and the pointer to the const
For constant pointers (const pointer) and pointer constants you should be able to make a clear point. Constant pointer: The value of the pointer itself can not be changed, can be interpreted as a const read-only, such as: int *const c_p; pointer constant: a constant of a pointer type, such as: (int *) 0x123456ff. Now introduce a new concept: pointer to const, which is a pointer to a const object, such as: const int *p_to_const; Indicates that P_to_const is a pointer to a constint variable, p_to_const itself can be changed, but cannot change the value of the object referred to by the P_to_const dereference, see the following example is more clear:
int *p = NULL; Defines an integer pointer and initializes it to null
int i = 0; Define an integer variable and initialize it to 0
const int CI = 0; Defines and initializes a read-only integer variable that cannot be assigned to a value in the program
const int *p_to_const = NULL; Defines a pointer to a read-only integer variable, initialized to null
p = &i; OK, let p point to integer variable i
P_to_const = &ci; OK, let P_to_const point to CI
*p = 5; OK, modify the value of I by pointer P
*p_to_const = 5; /*error,p_to_const is pointing to a read-only variable that cannot be p_to_const to the
CI to modify */
P_to_const = &i; OK to point a pointer to a const object to a normal object
P_to_const = p; OK, assign a pointer to a normal object to a pointer to a const object
p = (int *)? OK, force conversion to (int *) type, assignment operator on both sides of the same operand type
p = (int *) P_to_const; OK, ditto
p =? Error, causes of errors are described below
p = p_to_const; Error, ibid.
The assignment for the last two lines needs to be explained. The assignment of pointers in the C language (including the passing of real participation parameters) should satisfy: Two operands are pointers to either a qualifier or a type that is not a qualifier, or the type that the left pointer points to has all qualifiers for the type pointed to by the right hand pointer. For example, a const int * denotes a pointer to an int of type with a const qualifier, that is, const modifies the type that the pointer points to, not the pointer. So, p =? The &ic in is given a pointer to the const int variable, which is the same type as p_to_const. P_to_const points to a const int, and P points to a type int,p to the left of the assignment operator, P_to_const to the right of the assignment operator, and the left pointer to a type that does not have all qualifiers for the type pointed to by the right pointer, so an error occurs.
Small extension: { Let's go deeper, if there is now a pointer int **bp and a pointer const int **CBP Then the assignment is also wrong: CBP = BP; because the const int * * represents " Pointer to a pointer of type int with the const qualifier. int * * and constint are all pointer types without qualifiers, they point to different types (int * * points to int *, and Constint * * to const int *), so they are incompatible and judged by the pointer assignment condition. The two pointers cannot be assigned to each other.
The type that is actually compatible with the const int * * * is const INT**CONST, so the following code is legal:
const INT * *const Const_p_to_const = &p_to_const;
/* Defines a constant pointer to a pointer to an int type with a const qualifier, which must be initialized at the time of definition and no longer assignable in the program. Because neither the value of the pointer nor the value of the indicated object can be changed by the pointer, in practice this pointer is not widely used.
const int **cpp;
CPP = Const_p_to_const;
The left operand CPP points to a type that is const int*, the right operand const_p_to_const to a type, and a const int* that satisfies the pointer assignment condition: the type that the left pointer points to has all qualifiers for the type pointed to by the right pointer, except CONST_P_TO _const is a const pointer and cannot be re-assigned, so it cannot be assigned in the opposite way. Also note that const-qualified objects can only be and must be initialized at the time of declaration. }
4. Value passing in the C language
In the 3rd, it is mentioned that the C language only provides the function parameters of the call mechanism, that is, when the function is called, copy the copy of an argument and assign the copy to the parameter, since the actual participation parameter is irrelevant, the change of the formal parameter in the function does not affect the argument. As I said before, all data arguments (including pointers) in the C language that are non-array forms are called in the form of a value, and this is not inconsistent with the C language providing only a value call mechanism, and for arrays that are converted to pointers to the first element of the array, when we use array masterpieces as arguments, the actual value is passed. Please see the program:
#include
void Pass_by_value (char parameter[])
{
printf ("Value of formal parameter:%p\n", parameter);
printf ("Address of formal parameter:%p\n", ¶meter);
printf ("%s\n", parameter);
}
int main ()
{
CHARARGUMENT[100] = "C language only value call mechanism!" ";
printf ("Value of the argument:%p\n", argument);
Pass_by_value (argument);
Return0;
}
The output on my machine is: The value of the argument: 0022ff00
Value of formal parameter: 0022ff00
Address of formal parameter: 0022fed0
C language only value call mechanism!
When executing pass_by_value (argument), the real parameter group Name argument is converted to a pointer to the first element of the array, the value of this pointer is (void *) 0022ff00, and a copy of this value is assigned to the formal parameter parameter, Although the formal parameter parameter is declared as a character array, it is converted to a pointer, which is the copy that creates a separate object on the stack (it has its own independent address) and receives the argument value. Thus we see that the real participation parameter has the same value, and the formal parameter has a separate address. Let's look at a simple example:
#include
void Pointer_plus (char *p)
{
p+= 3;
}
int main ()
{
Char*a = "ABCD";
Pointer_plus (a);
printf ("%c\n", *a);
Return0;
}
If a friend thinks that the output is D, then you still do not understand the concept of value delivery, this program assigns a copy to P, since a and p are not related, in the function pointer_plus to increase the value of p actually increases the value of the copy of a, does not affect a, In the main function a still points to the first character of the string, so the output is a. If you want Pointer_plus to change the object a points to, use a level two pointer, the program is as follows:
#include
void Pointer_plus (char**p)
{
*p + = 3;
}
int main ()
{
Char*a = "ABCD";
Pointer_plus (&a);
printf ("%c\n", *a);
Return0;
}
5. Hanging hands (dangling pointer)
Hanging pointers are often used when we use pointers, so-called hanging pointers are pointers to indeterminate areas of memory, and usually manipulating such pointers can cause unpredictable errors in the program, so we should avoid hanging pointers in our programs, and some good programming habits can help us reduce the occurrence of such events.
The reason for hanging the pointer is usually divided into three kinds, we discuss it one by one.
The first: There is no initialization of a pointer when it is declared. The declared automatic variable is not initialized in the C language, so the default value of this pointer will be generated randomly, possibly pointing to the system-protected memory, at which point a run-time error is raised if the pointer is dereferenced. The workaround is to initialize the pointer when it is declared null or 0 pointer constant. You should get into the habit of initializing each newly created object, and some of the work you do at this point can be a lot less annoying.
Second: pointers to dynamically allocated memory are reused once they are free and not re-assigned. Just like the following code:
int *p = (int *) malloc (4);
*p = 10;
printf ("%d\n", *p);
Free (p);
......
......
printf ("%d\n", *p);
This can cause an error, first we declare a p and point to a dynamically allocated piece of memory space, and then assign a value to this space by P, and then release the memory that P points to by using the free () function. Note The purpose of the free function is to release P's memory space by pointer p and not release p, so the so-called release is to destroy the object in memory and return the memory to the system for his use. the value in the pointer p is still the first address of the memory, and if the memory is then assigned to store other values, the current value can be accessed by dereferencing p, but if the state of the memory is indeterminate, it may be protected, and perhaps no object is saved. At this point, a run-time error may occur if the P dereference is present, and this error is difficult to detect. So for security reasons, after the free pointer, set the pointer to null or 0 pointer constant. Although it is illegal to dereference a null pointer, if we do not care about the reference to the null pointer, the error is much more convenient to debug than to dereference a pointer to an unknown object because the error is predictable.
The third type: Returns a pointer to a local variable. The reason for the dangling pointer and the second similarity is that it creates a pointer to the object that once existed, but the object no longer exists. The difference is the reason why this object no longer exists. The second reason why this object no longer exists is that the memory is freed manually, and in the third reason because the pointer points to a local variable in a function, after the function ends, the local variable is automatically freed (without the programmer having to manually release it). As in the following program:
#include
#include
Int*return_pointer ()
{
int i=3;
int *p =&i;
return p;
}
int main ()
{
int *RP = Return_pointer ();
printf ("%d\n", *RP);
return 0;
}
In the Return_pointer function, a pointer p is created that points to the variable I within the function (the variable created inside the function is called a local variable) and the pointer is the return value. In the main function, there is a pointer to receive the return value of Return_pointer and then dereference and output it. The output may be 3 or 0 or other values at this time. The essential reason is that we return a pointer to a local variable, which is destroyed by the compiler at the end of the function, and the time of the destruction is determined by the compiler, so that p can point to memory that does not hold any object, or it may be a random value in memory, in short, the memory is indeterminate, P returns an invalid address.
C-Language pointer traps