A thorough understanding of pointer arrays, array pointers, and function pointers

Source: Internet
Author: User

 

I. Memory Allocation for pointers and heap
A pointer is a type of pointer. In theory, it contains the addresses of other variables, so it is also called address variable in some books. Since the pointer is of a type and has a size, the pointer size is 4 bytes in size on the Dana server or on a common PC, and only the address of a variable is stored in it. No matter what type of pointer, char *, int *, INT (*), string *, float *, it indicates the type of the address space pointed to by this pointer, after learning about this, it seems that all the problems have become reasonable.

In C ++, you can apply for and release the storage space allocated in the heap by using the new and delete operators respectively:
Pointer type pointer variable name = new pointer type (initialization );
Delete pointer name;
Example: 1. int * P = new int (0 );
It is roughly equivalent to the following code sequence:
2. Int TMP = 0, * P = & TMP;
Difference: the Variable P points to is allocated by the library operator new (), located in the heap area of the memory, and the object is not named.
  
The following is a description of the new operation: partially introduced from <C ++ object-oriented development>
1. The new operator returns a pointer to the allocated type variable (object. This pointer is used for indirect operations on the created variables or objects, but the dynamically created object itself has no name.
2. When defining variables and objects, you must name them with identifiers, named objects, and dynamically named unknown objects (note the difference between them and the temporary objects in the stack area. The two are completely different: the life cycle is different, the operation method is different, and the temporary variables are transparent to the programmer ).
3. The heap does not perform automatic initialization (including resetting) During allocation. Therefore, you must use initializer to explicitly initialize the heap. The Operation Sequence of the new expression is as follows: Allocate an object from the heap area, and then initialize the object with the value in brackets.

Apply for an array from the heap
1. Apply for array space:
Pointer variable name = new type name [subscript expression];
Note: "subscript expression" is not a constant expression, that is, its value does not need to be determined during compilation, but can be determined at runtime. This is a very notable feature of heap. Sometimes, when programmers do not know how much memory they can apply for, the heap becomes extremely useful.
2. Release array space:
Delete [] pointer variable name pointing to this array;
Note: square brackets are very important. If square brackets are missing in the delete statement, because the compiler considers the pointer to be the first element of the array, this will cause incomplete collection (only the space occupied by the first element is recycled). We usually call it "Memory leakage". After square brackets are added, it is converted to a pointer to an array, reclaim the entire array. The number of array elements is not required in square brackets of Delete. Even if it is written, the compiler ignores it. <Think in C ++> As mentioned above, the number must be added to the square brackets of the Delete [] statement. Later, due to errors, so later versions improved this defect.
The following is an example.
# Include <iostream>
Using namespace STD;
/// # Include <iostream. h> // for VC
# Include <string. h>
Void main (){
Int N;
Char * P;
Cout <"Enter the number of elements in the dynamic array" <Endl;
Cin> N; // n is determined at runtime. Enter 17.
P = new char [N]; // apply for memory space strcpy (PC, "dynamic heap memory allocation") with 17 characters (8 Chinese characters and an ending character can be loaded "); //
Cout <p <Endl;
Delete [] P; // release the memory space that the PC points to. Return ;}

Several possible problems in programming using pointers to heap Space

1. Dynamic Allocation failed. A null pointer is returned, indicating that an exception occurred, the heap resources are insufficient, and the allocation failed.
Data = new double

; // Apply for Space
If (data) = 0 )...... // OR = NULL
2. pointer deletion and heap space release. Deleting a pointer P (delete p;) actually means deleting the target (such as a variable or object) indicated by P, releasing the heap space occupied by P instead of deleting P itself, after the heap space is released, p becomes an empty pointer and cannot be used through P. P cannot be used directly before being assigned a value to P.
3. Memory leakage (Memory Leak) and repeated release. New is paired with Delete. Delete can only release heap space. If the pointer value returned by new is lost, the allocated heap space cannot be recycled, which is called Memory leakage. Repeated release of the same space is also dangerous because the space may have been allocated separately, if you release it again at this time, it will lead to a running error that is hard to be found. Therefore, the pointer returned by new must be properly saved to prevent memory leakage and ensure that heap memory space is not released repeatedly.
4. Life Cycle of dynamically allocated variables or objects. The life cycle of an unsung variable does not depend on setting up its scope. For example, a dynamic object created in a function can still be used after the function returns. We also call the free store as the heap space. However, you must remember that releasing this object occupies the heap space and can only be released once. It is created in the function. Releasing outside the function is a very easy task to get out of control and often leads to errors, so never apply for space in the function body and let the caller release it. This is a bad practice. How careful you are may also lead to errors.
Class to apply for memory in the heap:
The constructor must be called for the object created through new, and the Destructor must be called for deleting the object through deletee.
Cgoods * PC;
PC = new cgoods; // allocate heap space and construct an unknown object
// Cgoods object;
.......
Delete PC; // analyze the structure first, and then return the memory space to the heap. the life cycle of the heap object does not depend on the scope of the created object. Therefore, unless the program ends, the heap object (unknown object) will not expire, and you need to use the delete statement to parse the heap object explicitly. When the above heap object executes the delete statement, C ++ automatically calls its destructor.
Because constructors can have parameters, the new class type can also have parameters. These parameters are the parameters of the constructor.
However, if you create an array, there is no parameter and only the default constructor is called. See the following class description:

Class cgoods {
Char name [21];
Int amount;
Float price;
Float total_value;
Public:
Cgoods () {}; // default constructor. Because there are other constructors, the system will not automatically generate the default constructor. It must be explicitly declared. Cgoods (char * Name, int amount, float price ){
Strcpy (name, name );
Amount = amount;
Price = price;
Total_value = price * amount ;}
......}; // The class declaration ends.
The following is the call mechanism:

Void main (){
Int N;
Cgoods * PC, * pC1, * PC2;
PC = new cgoods ("Hello );
// Call the three-parameter constructor pC1 = new cgoods (); // call the default constructor cout <"Number of input commodity array elements" <Endl;
Cin> N;
PC2 = new cgoods [N];
// Dynamically create an array and cannot be initialized. The default constructor is called N times.
......
Delete PC;
Delete pC1;
Delete [] PC2 ;}

Constructor runs after applying for heap space;
Destructor run before the heap space is released;
Once again, the object array created by the heap area can only call the default constructor, and cannot call any other constructor. If no default constructor exists, the object array cannot be created.

------------------- Let's take a look at the pointer array and array pointer ―――――――――――――
If you want to know the pointer, you 'd better understand the following formula:
(1) int * PTR; // The Pointer Points to an int type.

(2) char * PTR; // The Pointer Points to a char type.

(3) int ** PTR; // The type pointed to by the pointer is int * (that is, an int * pointer)

(4) int (* PTR) [3]; // The type pointed to by the pointer is int () [3] // two-dimensional pointer Declaration

(1) pointer array: A pointer of the same type is stored in an array, which is usually called a pointer array.
For example, int * A [10]; it contains 10 int * type variables. As it is an array, it has allocated 10 (int *) spaces in the stack area, that is, the 32-bit host has 40 bytes, and each space can store the address of an int-type variable. At this time, you can initialize each element of this array, or initialize it in a separate loop.
Example:
Int * A [2] = {New int (3), new int (4)}; // declare an int * array in the stack, every element of it applies for an unknown variable in the heap and initializes them as 3 and 4. Note that this declaration method is flawed and an error will be reported in VC.
For example:
Int * A [2] = {New int [3], new int [3]};
Delete A [0];
Delet A [10];
However, I do not recommend that students of Dana write this statement, which may lead to ambiguity, not a good style, and report an error in VC. It should be written as follows:
Int * A [2];
A [0] = new int [3];
A [1] = new int [3];
Delete A [0];
Delet A [10];
In this way, the style of applying for memory is quite in line with everyone's habits. Because it is an array, You cannot delete a; the compilation will issue a warning. delete a [1];
Note that this is an array and cannot be deleted [];
(2) array pointer: a pointer to a one-dimensional or multi-dimensional array;
Int * B = new int [10]; pointer to the one-dimensional array B;
Note: delete [] is required to release space at this time. Otherwise, memory leakage will occur, and B will become an empty hanging pointer.

INT (* B2) [10] = new int [10] [10]; note that B2 points to the first address of a two-dimensional int array.
Note: Here, B2 is equivalent to a two-dimensional array name, but its boundary is not specified, that is, the number of elements in the highest dimension. However, the number of elements in the lowest dimension must be specified! Just like a pointer to a character, which is equivalent to a string, do not say a pointer to a character as a pointer to a string. This is consistent with the nested definition of the array.
INT (* B3) [30] [20]; // Level 3 pointer-> pointer to a 3D array;
INT (* B2) [20]; // second-level pointer;
B3 = new int [1] [20] [30];
B2 = new int [30] [20];
Both arrays are composed of 600 integers. The former is a three-dimensional array with only one element, each element is a two-dimensional array with 30 rows and 20 columns, and the other is a two-dimensional array with 30 elements, each element is a one-dimensional array of 20 elements.
You can delete these two dynamic arrays as follows:
Delete [] B3; // Delete (release) a 3D array;
Delete [] B2; // Delete (release) two-dimensional array;
Again, the B2 type here is int (*), which indicates a pointer to a two-dimensional array.
B3 indicates a pointer (pointer to a two-dimensional array), that is, a three-level pointer.

(3) pointer of level 2 pointer
See the following example:
INT (** p) [2] = new (INT (*) [3]) [2];
P [0] = new int [2] [2];
P [1] = new int [2] [2];
P [2] = new int [2] [2];
Delete [] P [0];
Delete [] P [1];
Delete [] P [2];
Delete [] P;
Note that the pointer type in this area is int (*). If this problem occurs, remove [2] from the outside, and then switch back to int ** P = new int (*) [N] apply, and then append the external [2;
P indicates a pointer pointing to a second-level pointer. When applying for space, P indicates the pointer type, that is, INT (*) indicates a second-level pointer, and INT (**) indicates a second-level pointer (**) as the name implies, it indicates a pointer to a second-level pointer. Since a pointer needs to apply for space in the heap, first define its range: (INT (*) [N]) [2], n such second-level pointers, the lowest dimension of each second-level pointer is two elements. (Because to determine a second-level pointer, its lowest dimension must be specified, as mentioned above ). Then we are P [0], p [1], p [2]… Allocate space in the heap. Note that when releasing the memory, it must be P [0], p [1], p [2], delete [] separately. Otherwise, memory leakage may occur. When you delete [] P, delete p [0]; Delete P [1] first. then release the space applied for P. Delete [] P ...... This will prevent memory leakage.

(3) pointer;
Int ** cc = new (int *) [10]; declares an array of 10 elements. Each element of the array is an int * pointer, and each element can apply for space separately, because the CC type is an int * pointer, you need to apply for it in the heap;
See the example below (VC & GNU Compiler have passed );
Int ** A = new int * [2]; // apply for two int * types of space
A [1] = new int [3]; // the second element of a applies for three int-type spaces. A [1] points to the first address of the space.
A [0] = new int [4]; // the first element of a applies for four int-type spaces, A [0] points to the first address of the space
Int * B;
A [0] [0] = 0;
A [0] [1] = 1;
B = A [0];
Delete [] a [0] // The space of a [0], a [1] must be released first; otherwise, memory leakage may occur .;
Delete [] a [1];
Delete [];
B ++;
Cout <* B <Endl; // Random Number
Note: Because A is an unknown variable array applied in the heap, delete [] is used to release the memory during the delete operation, but each element of a separately applies for space, therefore, before Delete [] A, delete [] a [0], a [1], or cause memory leakage.
(4) pointer array:
   
Let's take a look at the second type: Two-Dimensional pointer Array
Int * (* C) [3] = new int * [3] [2];
If you are familiar with the pointer type described above, you can see at a glance that C is a second-level pointer, just pointing to a two-dimensional int * array, that is, a two-dimensional pointer array.
Example:
Int * (* B) [10] = new int * [2] [10]; //
B [0] [0] = new int [100];
B [0] [1] = new int [2, 100];
* B [0] [0] = 1;
Cout <* B [0] [0] <Endl; // print the result as 1
Delete [] B [0] [0];
Delete [] B [0] [1];
Delete [] B;
Cout <* B [0] [0] <Endl; // print a random number
We will only pay attention to the memory leakage issue here, so we will not talk about it here.
If you read the above article, you will probably be familiar with it. B is a two-dimensional pointer that points to an array of pointers.

Second:
Int ** d [2]; indicates an array of two elements, each of which is int ** type. This pointer to the pointer :)
D. No matter how it changes, it is also an array,
If you understand the above, the following statement is very simple:
D [0] = new int * [10];
D [1] = new int * [10];
Delete [] d [0];
Delete [] d [1];
I will not talk about it more specifically :)
Ii. function pointer

For function pointers, I think we may need to write a function, which calls another function in the body. However, due to the limited progress of the project, we don't know what function to call, A function pointer may be required at this time;

Int A (); Declaration of this function;
Ing (* B) (); this is a declaration of a function pointer;
Let's analyze that the asterisks in the left Circular Arc are the key to function pointer declaration. The other two elements are the return type (void) of the function and the entry parameter in the incircle arc (in this example, the parameter is null ). Note that no pointer variable is created in this example-only the variable type is declared. Currently, you can use this variable type to create a type definition name and use the sizeof expression to obtain the size of the function pointer:
Unsigned psize = sizeof (INT (*) (); returns the size of the function pointer.
// Define the function pointer declaration type
Typedef int (* pfunc )();

Pfunc is a function pointer that points to a function without any input parameter and returns an int. Using this type definition name can hide the complex function pointer syntax. I strongly recommend that you use this method to define it;

The following is an example of a simple function pointer callback (via the GNU Compiler, it is OK to change a header file on the VC)

# Include <iostream> // GNU Compiler g ++ implementation
Using namespace STD;
/* // VC implementation
# Include "stdafx. H"
# Include <iostream. h>
*/

# Define DF (f) int F () {cout <"this is in function" <# F <Endl ;/
Return 0 ;/
}
// Declare and define DF (f) to replace int F (); function;
DF (a); df (B); df (c); df (d); df (E); df (f); df (g); df (h ); DF (I); // declare the definition function a B c d e f g h I

// Int (* pfunc) (); // declaration of a simple function pointer
Typedef int (* func) (); // declaration of a function pointer type

Func FF [] = {A, B, C, D, E, F, G, H, I}; // declare a function pointer array, and initialize the, B, C, D, E, F, G, H, I functions declared above.

Func func3 (func vv) {// define the function func3, pass in a function pointer, and return a function pointer of the same type
VV ();
Return vv;
}

/* Func func4 (INT (* vv) () {// another Implementation of func3
VV ();
Return vv;
}*/

Int main (){
For (INT I = 0; I <sizeof (FF)/sizeof (func); I ++) {// call the function pointer cyclically
Func r = func3 (FF [I]);
Cout <R () <Endl; // return value, but 0 is returned.
}
Return 0;
}
So far, we have only discussed function pointers and callbacks, but have not paid attention to the ansi c/C ++ compiler specifications. Many compilers have several call specifications. For example, in Visual C ++, you can add _ cdecl, _ stdcall, or _ Pascal before the function type to indicate its call specifications (default value: _ cdecl ). C ++ builder also supports the _ fastcall call specification. The call specification affects the given function name generated by the compiler, the sequence of parameter passing (from right to left or from left to right), and the responsibility for Stack cleaning (caller or called) and parameter transfer mechanism (stack, CPU register, etc ).
Well, it takes almost half a day to write this article. Many things have not been done yet, and I will come back to sort it out after the next day, all source programs are stored in the LIB/CPP directory on the openlab3 server. You can get them. Log on to openlab3 and then Cd ~ Chengx/lib/CPP.

A complicated statement may also be a challenge, for example, in <think in C ++>
INT (* F4 () [10] (); Declaration. F4 is a function that returns a pointer. This pointer points to an array containing 10 function pointers, these functions return an integer value. It is not a special feature of this function. It is also a good way for Bruce Eckel to say "recognize rules from the right to the left". It is worth learning, thanks to him :)

Finally, I would like to tell you that writing a program should be like what Jerry said: simplicity is beauty; we should follow a principle: Kiss (Keep it simple, stupid, keep the program simple as possible: Practical C Programming). It is a very good habit to make your program as simple and clear as possible.


 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.