Pointer and its semantics and Application
This article is an appendix to the "C ++ from scratch" series. As friends once and again think that the pointer in the "C ++ from scratch" series is too simple, and the various concepts proposed are mixed, in addition, pointer is rarely used as an important concept in C ++, therefore, this article focuses on the basic concepts of numbers, addresses, and pointers proposed in the C ++ from scratch series, and provides the semantics of pointers to illustrate the relationship between pointers and arrays, describes the semantics and Application of Multi-level pointers, multi-dimensional arrays, function pointers, array pointers, and member pointers.
Number, operator, type, type Modifier
In "C ++ from scratch (3)", it has been stated that in fact, the CPU is not even aware of the binary number, and it can only process the status, the status that it can process can be expressed by the binary number. Therefore, the CPU only recognizes the binary number. Attention should be paid to the fact that the CPU recognizes the binary number as the State indicated by it, rather than the binary number in the mathematical sense. Therefore, the CPU does not recognize the decimal number 20. However, after 20 is converted to a binary number of 10100 according to mathematical rules, a good-luck CPU designer defines the addition command as State 10100 and State 10100, and then adds state 101000, this binary number is converted to a decimal number by mathematical rules, which is exactly 40. That is, the CPU does not even perform addition, subtraction, multiplication, division, but changes the status in different ways, the CPU designer defines the changing methods of those States and performs the same addition, subtraction, multiplication, division, and so on.
Therefore, in order for the CPU to execute an instruction, the instruction can only involve binary numbers, so there must be rules to convert the given mathematical value to binary. For example, in the preceding decimal to binary rules, the original code, complement code, and IEEE real * 4 mentioned in C ++ from scratch (II. To write c ++ code, the above conversion rules must be embodied in the Code, and the number of mathematical values to be converted must be reflected in the Code, in this way, the compiler can determine the binary representation of CPU operations based on the code we write. In this regard, C ++ uses the type to represent the former, uses numbers to represent the latter, and uses operators to represent CPU instructions, that is, the way the CPU status is changed.
Therefore, in order for the CPU to execute the addition command, the corresponding operator "+" and "+" in the Code must be followed by two numbers, the number type determines how to convert the number represented by a number into a binary number. It should be noted that numbers are compiled-level concepts, not code-level concepts, that is, numbers cannot be expressed in the code, but can only be obtained through the return of the operator's computation. Because any operator must return a number (the operator that does not return a number can also be expressed by a number of the void type to satisfy this statement ), the most common operator for getting a number is something that is often called a constant, such as 6.3, 5.2f, and 0772. In C ++ from the very beginning (II), I refer to it as a number that does cause conceptual confusion. I would like to clarify here.
It should be noted that as long as the return number is an operator, the previous constant is also an operator. Variables, member variables, and functions have been repeatedly stressed in the C ++ from scratch series that they are all ing elements, directly write the variable name, member variable name, and function name to return the numbers mapped to each other. That is, variable name letters and numbers are also operators.
Numbers are of different types. c ++ provides custom types such as struct and class to customize complex types, c ++ also provides a more commendable type modifier. As stated in C ++ from scratch (5), type modifiers are used to modify types, that is, to change the type to be modified (called the original type) according to certain rules) number conversion rules. For example, the "blood" and "meat" Here are type modifiers, changing their original types-"pig" and "goat ". The above feeling is more like the latter to modify the former than the former to modify the latter, for example, the "blood" in Swine Blood is the subject and the "Swine" is the attribute. That is, the type modifier modifies the numeric conversion rule that it represents based on the original type information. This is like "blood" and "meat". It also means that some types are pointer type, reference type, array type, function type, etc.
In the C ++ from scratch series, the following types of modifiers are proposed: Reference "&", Pointer "*", array "[]", function "() ", function call rule" _ stdcall ", offset" <custom type name >:: ", constant" const ", and address type modifier. The address type modifier is the most confusing.
In C ++ from scratch (3), the address is a number in a 32-bit operating system, which is often expressed as a 32-bit long binary number, uniquely identifies a specific memory unit. When a number is of the address type (because there is an address type modifier, it seems that a number is of the array type ), the number represented by this number is represented in binary to identify a memory unit, then explain the content of the memory unit and its subsequent units according to the rules of the original type (the type length may be more than one byte, and the address type is a type modifier, so the original type must exist ). Because the number of ing variables is actually addresses, the numbers mapped to the variables are addresses. For example, long a; assume that a maps to 3006. When a = 3; is written, the number 3006 mapped to a is returned because a is the variable name, the type is long. Because it is an address type, the syntax check of the "=" operator is successful (this is another use of the Type-syntax check, just like the dynamic name adjective), and the calculation of the "=" operator is executed.
It should be noted that C ++ did not propose the concept of address type modifier, But I proposed it out of complete syntax. Otherwise, more unnecessary concepts and rules should be involved, for example, * (p + 1) = 20; A [2] = 3; and so on will be complicated, therefore, in the "C ++ from scratch" series, the concept of address-type numbers is proposed to explain as many syntaxes as possible with as few concepts as possible.
The most common type modifier-pointers and Arrays
In "C ++ from scratch (5)", it has been stated that the pointer is only a type modifier. When a number is of the pointer type, the number represented by this number is represented in binary and returned. As mentioned above, the use of a number is to convert the number to the binary number. Its type only describes how to convert the number. The pointer type represents a rule that converts the number to the binary number according to mathematical rules, regardless of the original type. Because it is not affected by the original type, the pointer type is always fixed length (not necessarily for member pointers), which is four bytes in a 32-bit operating system.
For example, long a; long * P = & ;. Assume that a maps 3006, and p maps 3010. For * P = 3;, the p operator returns the number 3010 of the address type of the long pointer type, that is, the number type is modified twice by two type modifiers, because it is finally modified by the address, 3010 is the number of the address type, and its original type is the long pointer type. Therefore, * P returns the number 3006 of the long-type address type, and then runs the "=" operator.
Here, pay attention to the two operators-the content operator "*" and the address operator "&". In "C ++ from scratch (5)", we also stressed that they are actually not real-name, and should be called type conversion operators. That is, the former is followed by a number of the pointer type, and the returned number is not moved, only the type is changed to the address type; the latter is followed by a number of the address type, the returned number is not moved, only change its type to pointer type. This is a bit clever, but please note: Long * P1 = 0; long * P2 = & * P1;
If the "*" operation is to retrieve the content, & * P1 will first take the content of the memory unit with the address 0, which will cause memory access violations, but it does not, because "*" only converts the type rather than the content (the content is calculated by the number of the address type ).
As mentioned above, when a number of pointer types returns binary numbers, the original type does not need to be involved, that is, the number 3006 of the long * type and the number 3006 of the char * type return the same binary number, which is the binary number corresponding to 3006. So why should we make the pointer A type Modifier with an unused original type? We can see from the above that the original type of the pointer type is used for the content operator. However, it is useful because the addition of the array type modifier adds a so-called arithmetic function to the pointer. Let's look at the array type first.
In "C ++ from scratch (5)", the modification function of the array is to store the elements of the original type repeatedly multiple consecutively to form a new type. For example, long a [10]; A is of the long [10] type and the length is 10 * sizeof (long) = 40 bytes, the length of the char [7] type is 7 * sizeof (char) = 7 bytes. When a number is of the array type, the length of the number can be one byte and 10 thousand bytes. Therefore, the number must be stored in a memory, the binary number returned by array-type numbers is the first address of the memory to be stored. Therefore, the constant mentioned above cannot return an array-type number because it does not provide a piece of memory to store array-type numbers.
This is a bit confusing. Note that numbers do not have to be stored in the memory. For long a [3] = {45, 45, 45};, assume that the number mapped to a is 3000, it indicates that the number recorded in memory unit 3000 is interpreted according to the rule of long [3]. The length of this number is 3 * sizeof (long) = 12 bytes, its value is determined to use 3000 (the address that records its memory) because the array type is variable in length, which is actually the number of three values 45. Therefore, a; first returns the number 3000 of the long [3] type address type, then calculates the number of this address type and returns the number of its original type, since the original type is long [3], and this number is stored in the memory identified by 3000, the binary number corresponding to 3000 is returned.
It is easy to find that the pointer returns an address, and the array is also an address. When they are of the same type, the latter can be implicitly converted to the former, but vice versa, because the array also has the number of elements, that is, the original type of long [2] and long [3] is the same, but the type is different. Therefore, long a [3]; long * P = ;. There is no problem here. If a is mapped to 3000, the value of P is 3000. Therefore, * P = 3; put 3 into the 3000 elements of the array-type number stored at 0th. To put 1st and 2nd elements, C ++ provides a so-called pointer operation function, as follows: * (p + 1) = 4; * (p + 2) = 5 ;. Put 4 to 1st elements, and 5 to 2nd elements. For * (p + 1) = 4;, P returns a long * Number 3000, while p + 1 returns the long * Number 3004, and then continues the subsequent computation. Similarly, P + 2 returns the number 3000 + 2 * sizeof (long) = 3008 of the long * type. That is, the pointer can only be used to add or subtract integers, for example:
Char * P1 = 0; P1 ++; P1 = p1 + 5*8-1; short * P2 = 0; p2 + = 11; P2 --;
The value of P1 is 40, and the value of P2 is 40, because the original type of P1 is Char, and that of P2 is short.
Therefore, * (p + 2) is required to obtain the values of the 2nd elements in the array, which is obviously not easy to read, therefore, C ++ provides a subscript operator "[]", which is followed by a number of the pointer type. An integer number is placed in square brackets to replace the number of the pointer type with the address type, then convert the value according to the pointer operation rule mentioned above and return the result. For example, long a [4]; long * P = A;. Assume that A is mapped to 3000. Then a [2] = 1; equivalent to * (p + 2) = 1;, a [2] is followed by a number of the long * type 3000 (implicit type conversion, convert from long [4] to long *), add 2 * sizeof (long), and return 3008. The type is simply converted to the address type of long. Because "[]" is only a simplified and easy-to-read version of the previously mentioned pointer operation, it can also be a [-1] = 3;, which is equivalent to * (p-1) = 3 ;. Because "[]" is followed by a pointer, P [-1] = 3; is also supported, which is equivalent to a [-1] = 3 ;.
Repeated modifier-multi-level pointer and multi-dimensional array
In the aforementioned type modifiers, only the pointer, array, and offset types can be repeatedly modified consecutively. Repeated modification of offset to indicate type nesting. Repeated modification of pointer is called multi-level pointer, and repeated array is called multi-dimensional array. This seems to be complicated, but it is not actually used. Just remember that the type modifier modifies an original type to form a new type that can be considered as the original type.
Long ** P ;. If the number mapped to P is a long ** address number, then P; the returned number is long **. For * P;, You can regard the number type returned by P as the pointer type of long *. The original type is long *, therefore, the number type returned by * P is the number of the long * address type, while ** P; returns the number of the Long Address type.
The same applies to long a [2] [3. Since its original type is long [3], it can be long (* P) [3] = ;, then a [1] returns a number of the long [3] address type, while a [1] [2] returns a number of the Long Address type.
Note that the original type of A is not long [2] But long [3]. It has been stated in "C ++ from scratch (5)" That when multiple types of modifiers are simultaneously modified, the modifier order is from left to right, the modifier of the same continuous modifier is in line with people's habits from right to left, while "()" reduces the modifier priority. For example, long * [2] [3] is a long pointer type array [3] type array [2] type, that is, it is an array type and has two elements, the original type is long * [3]. That is, the final modification of "[2]" has the lowest priority (in terms of the type of the number it modifies, it can also be considered as the highest priority ). Long * [3] * [2] [3] is an incorrect type. Although there is no problem in modifying from left to right according to the preceding descriptions, however, C ++ specifies that the pointer type modifier must be on the left side of the variable name, And the array type modifier must be on the right side to conform to reading habits, therefore, "()" appears in the type to reduce the modification priority. Therefore, long * [3] * [2] [3] should be written as long * (* [2] [3]) [3], to define a variable, long * (* P [2] [3]) [3]; where the last modifier is "[2]" (not "[3]", because the modifier of the same continuous modifier is from right to left rather than from left to right ). Therefore, P is an array with two elements. The original type is long * (* [3]) [3], while long * (* pp) [2] [3]) [3] = & P; no problem, because the last modification is "*", the original type is long * (* [2] [3]) [3].
In addition, pay attention to one thing-no matter what multi-level pointer, its length is 4 bytes (this is for 32-bit operating systems, and the member pointer may be more than 4 bytes ), but the more dimensions the array has, the longer the type length (of course, if the number of elements is 1, the length remains unchanged ). For example, long *** P; at this time, only four bytes of memory space are allocated, while long a [2] [3] [5]; allocate 2*3*5 * sizeof (long) = 120 bytes of memory. As follows:
Long a = 0; long * P = & A, ** pp = & P; long B [2] [3] [4];
Assume that the above a ing is 3000, p maps 3004, pp maps 3008, and B maps 3012. After the above assignment, the value of a is 0, the type is long; the value of P is 3000, the type is long *; the value of PP is 3004, And the type is long **; the value of B is 3012 and the type is long [2] [3] [4].
For * (PP + 1) = 5;, pp returns the number 3004 of the long ** type, while the length of the original long * type is 4 bytes, therefore, pp + 1 returns the number 3008 of the long ** type, while * (PP + 1) only converts the type, and returns the number 3008 of the address type of the long * type, returns the number 3004 of the long * type, so * (PP + 1) returns the number 3004 of the Long TYPE address type, and * (PP + 1 )) = 5; then, 5 is placed in the memory marked by 3004 according to the long storage rule, and the result P value is changed to 5 instead of 3000 (luck 5 is a positive number, at this time, the numeric conversion rule of the long type is the same as that of long *. If * p = 1 is continued, the error is returned (note that the above assumes that the compiler order is placed a, P, and pp, the PP address is 4 more than P. If different compilation settings and compilers do not necessarily place local variables in the above sequence, * (PP + 1) = 5; may fail ).
For * (B + 1) + 1) + 1) = 5;, B returns the number 3012 of the long [2] [3] [4] type, if the original type is long [3] [4], B + 1 will implicitly convert the type first to convert 3012 to long (*) [3] [4], while sizeof (long [3] [4]) = 48 bytes, B + 1 will return the type long (*) [3] [4]: The number 3012 + 48 = 3060, while * (B + 1) returns the number 3060 of the address type of long [3] [4, then return the number 3060 of the long [3] [4] type. * (B + 1) + 1 returns the number 3060 + sizeof (long [4]) = 3076 of the long (*) [4] type. Likewise, * (B + 1) + 1) + 1) returns the number 3076 + sizeof (long) = 3080 for the Long TYPE address, put 5 in the memory identified by 3080. From the preceding description of "[]", we can see that * (B + 1) + 1) + 1) = 5; it is equivalent to B [1] [1] [1] = 5; and can be self-verified as above. It should be noted that although B is a multi-dimensional array, it is still a contiguous memory space.
Why do we need multi-level pointers and multi-dimensional arrays? Long a [3] [4]; and long a [12]; both allocate a continuous 48-byte memory space. What is the difference between them? When will the former be used? In the "C ++ from scratch" series, it is emphasized that programs should be compiled by semantics, so as long as the semantics of pointers and arrays are clearly defined, they can be used in a systematic manner.
Semantics and Application of Arrays-vectors and containers
A type is composed of several elements of the same type. This is the semantics of the array type modifier. This can be used to map vectors in linear algebra. For example, the coordinate of a point on a two-dimensional plane is a two-dimensional vector. Assume that double is used to record the component of the coordinate of a point, then double A [2]; we can consider that a two-dimensional coordinate is defined. If you want to be more readable, typedef double point_2d [2]; point_2d ;.
In reality, it is easy to find that an array actually implements a set, which can be used as a container to record multiple numbers of the same type. In this case, the semantics of the array type-vector-has been ignored. The focus is on the defined array variables, that is, the focus is not point_2d, but. For example, double container [300]; The semantics is embodied by the variable name instead of the type. Writing a double container [300] as a container light is not enough, because it cannot know which elements are valid and which are invalid, so other variables need to be involved to complete the basic functions of a container. In practice, a class is often written to hide the details of the above implementation of the basic functions of the container. The Semantics of this class ing is the container, which is generally called the container class or collection class, for example, vector in STL. This type is also called the encapsulation class for the array container.
What is the semantics of multi-dimensional arrays? What is the significance? This is actually very simple, because the array is a type modifier, you don't have to worry about its one-dimensional or multi-dimensional, just know that it is a type consisting of multiple original types of elements. For example, long a [2] [3]; can be mapped to a two-dimensional vector, but each component is a three-dimensional vector, which can map a matrix in linear algebra. Therefore, typedef matrix_23 [2] [3]; matrix_23 ;. Similarly, you can only focus on defined array variables and ignore the semantics of the types, such as double A [300] [300]. Here, what is the difference between a and double B [90000]; defined B? There is no difference in the memory operated at the end, but only the semantic difference-the container represented by the former requires two keywords (key) to locate an element (value ), the latter can locate the element with only one keyword (A [2] [3] = 3; equivalent to B [603] = 3;, but the former is less efficient ). The former is like a table in a workbook. Only the horizontal and vertical coordinates are given to locate the location of the data to be written. The latter is a general continuous container. You can also focus on both the type semantics of Arrays-vectors and the instance semantics of Arrays-containers, such as point_2d pcontainer [300];, the pcontainer type is actually double [300] [2].
References and indirect
To describe the semantics and usage of pointers, it is best to first understand references and indirect. The reference here does not refer to the reference type modifier "&" in C ++, but a language-independent concept. In C ++) it has been clarified in detail in this article: Reference represents a connection relationship, so that the original purpose can be achieved by some means, and now another means can be achieved, this method is often advantageous in some aspects than the original method. For example, a person's mobile phone number is a reference to a conversation with someone. The library's collection number is a reference to a book. A person's name is a reference to someone. What is the purpose of reference? It is useless, and there must be some means to achieve its "equivalent" purpose. If a person's mobile phone is not present or has no power, the "mobile phone number" cannot complete the function equivalent to "talking to someone". If there is no correct discharge book, the book number is meaningless; without a complete search system, the name of the person given cannot be found. This is because reference is an "indirect" part. In "C ++ from (11)", I explained what indirect means, the following describes the three advantages: simplifying operations, improving efficiency (in some aspects), and increasing flexibility.
When talking to someone via a mobile phone, you can talk to someone via a mobile phone. When you look at a landscape photo, you can see the scenery indirectly through a photo. When selling a product, you can sell it indirectly (not by the boss ). That is to say, there is a "primitive means" that can achieve the goal, and there is a "Advanced means" that can manipulate that "primitive means". It is indirect to achieve the goal through "Advanced means, the configuration information of "Advanced means" (indicating how to use "Advanced means") is reference. The so-called method is a method or function, and the configuration information of the method is used to improve the application of the method. For example, a speech is a method, and a speaker is a quote, that is, a speech is quoted, indicating that a speech can be made as long as you have an individual. "Voice is transmitted to people's ears" is "original means", "mobile phones can transmit voice to people's ears" is "Advanced means ", "Mobile Phone Number" determines how the phone transmits the sound (to which phone number); "the light reflected by the landscape enters the eyes" is the "original means ", "The light reflected by the photo enters the eyes" is an "Advanced means", "the photo" determines how to reflect the light, and "the salesman can sell the goods" is a "original means ", "A salesperson can be ordered to sell goods" is an "Advanced means". "A salesperson" determines how to order a salesperson to sell goods (the command ). "Mobile Phone Number" references "talking to someone", "photo" references "Landscape", and "salesman" references "selling goods ".
It should be noted that referencing itself is also a kind of resource, and it can be operated. When it is modified, different results will be obtained for the execution of the same "Advanced means, therefore, flexibility can be improved indirectly. That is, the reason why the flexibility can be improved is that the "advanced means" can be configured. If the configuration is not available, the flexibility cannot be improved indirectly.
It should also be noted that "original means" may be another indirect "Advanced means", such as "a" asking B for money, and "B" command "C" selling goods to get money, that is, indirect means. If both levels of indirect "Advanced means" can be configured, the level-2 indirect flexibility may be higher than the level-1 indirect (only possible, depends on whether the "configurable" feature is used ).
Efficiency is reduced. Because the original "original means" can achieve the goal, but now we need to implement "Advanced means" and "original means" to achieve the goal. What is the indirect significance? It must be because "Advanced means" is better than "original means" in some respects ". For example, for simplified operations, such as window packaging cwnd in MFC, the use of advanced means can be configured to improve flexibility; the "original means" cannot be implemented, but the "Advanced means" must be used. If a has no time to send B to negotiate with the customer; in terms of management, "Advanced means" is given a certain meaning to simplify management, which is equivalent to classification and hierarchy. For example, the marketing department sells goods instead of sales personnel.
Pointer semantics-Reference
So many of the above, what is the pointer? The semantics of the pointer is reference. In the "C ++ from scratch" series, the program is the description of the method, the method is the description of how to operate the resource, and the resource that can be operated in C ++ is only a number, the number is stored in a memory according to the rule of its type. The memory block is identified by its first address. As mentioned above, the calculation of address-type numbers will take content operations, while the calculation of pointer-type numbers will not take content. directly return the address, that is, a pointer-type number is an address, the address is the only resource that can be operated in C ++ -- number -- the location where the resource is stored, that is, the reference of a memory block.
Since a pointer is referred to as a reference, it is indirect. The Calculation of numbers (that is, return the corresponding binary number according to the rule defined by the number type) is the "original method", the pointer is a reference, the "advanced method" is to take the content (replace the pointer type with the address type by taking the content operator ). It should be noted that the reference may also not use the "advanced means" to obtain the content, or others, but it is necessary to ensure that this "Advanced means" can be controlled (or executed) "original method"-number calculation (that is, all the places where expressions can be placed in the C ++ syntax, the pointer type to the address type is exactly implemented through the operator form, can be placed in the expression to achieve the purpose of executing the "original means"), such as type conversion. Now we understand the indirect introduced by pointers. Next we will look at how to use this indirect.
What are the advantages of "Advanced means" in retrieving content over "original means" in computing numbers? The retrieved content has a unique configurable information-the location of the retrieved content, that is, the address. As mentioned above, the configuration of "Advanced means" brings flexibility as follows:
Long a1 = 1, a2 = 2, A3 = 3, * p = 0;/* some operations determine the value of P */* P + = 5;
The above flexibility is reflected in multiple executions * P + = 5; this statement may increase the value in different memory by 5. However, if A2 + = 5;, the memory value identified by the address corresponding to A2 is increased by 5 no matter how many times it is executed. This is because the pointer is a reference to an address number.
It should be noted that the address type also brings an indirect, which is a digital reference. The Calculation of numbers is "original calculation", and the content is "Advanced means" (The feeling is the same as the pointer, but the latter needs to take the conversion of the content operator instead of the former ), the number of the address type is a reference. This leads to A3 = A1 + A1; it is also flexible, because it is not a3 = 1 + 1; but the address indirectly obtains the long type number. For a3 = * P + * P, the address is obtained indirectly, and then the number in the memory is obtained indirectly. This is a level-2 indirect, and its flexibility is higher than a3 = A1 + A1 ;, therefore, a pointer can be a reference to a number.
The indirect simplified operations and other functions mentioned above require the support of "Advanced means". Here, we only use content operators in general, so pointers are mainly used to increase flexibility.
Note that the reference is generally a small resource, which is easy to record before being considered as a reference. For example, you only need to give B's mobile phone number to a instead of bringing B to a to talk to a and B. When referencing an operation on a certain resource, the cost of providing a small reference is less than that of providing an instance of that type of resource. In this case, transferring a reference is more efficient than passing an instance of that type of resource, this is what we call efficiency improvement. This is very popular in C ++, which relies entirely on the second-level indirect pointer to reference numbers. That is, a number is very large, such as a length of 300 bytes (for example, char [300]), and its reference-address-only 4 bytes, therefore, the address should be transferred when it is passed to the function, and the number of the pointer type is the address, so the parameter type of the function should be a pointer (char (*) [300]) instead of the original type (char [300]), the transfer cost of the latter is higher.
Usage of Pointers-indirect
So far, according to the above four usage purposes that can be drawn from the pointer-increasing flexibility, referencing memory blocks, referencing numbers, and semantic needs are described as follows:
Increased flexibilityWhen writing a piece of code, especially a loop, pointers are often used in the loop body, because the flexibility can be reflected, that is, the same approach gets different results, therefore, different results are obtained when the same loop body is executed repeatedly to simplify programming (otherwise, a piece of code needs to be written for each result ). To increase the value of 100 discrete 20-byte memory blocks, place the first address in a pointer, as shown below:
Long * P;/* initialize p */For (long I = 0; I <100; I ++, P + = 5) * P ++;
Therefore, pointers are often used to increase the flexibility of code that you do not want or cannot change. Such as the function body, library file (such as dynamic Connection Library), and module (that is, the program structure, such as the game logic computing module and drawing module.
Note the existence of function pointers. A prototype of a function represents a call rule, that is, the interface mentioned in C ++ from scratch (18), then the function pointer is an interface reference, when two modules (or components) are connected, for example, the logic computing module calls void draw (); to draw, but in the drawing module (that is, inside the draw function) A global function pointer void (* g_pdraw) () can be used to determine different drawing methods based on the current drawing data (such as complex scenarios )();, each time the drawing module sets this variable based on the drawing, the logic computing module changes to (* g_pdraw) () instead of draw (); (this method is not recommended, as an example ).
For member pointers, in "C ++ from scratch (9)", it has been pointed out that non-static member variables are mapped to offsets rather than addresses, that is, member variable pointers are not referenced by memory blocks, it is only a reference of a number of the Offset type. The pointer of a number of the Offset type is 8 bytes long, so it is not used for optimization of the previous transfer, it cannot reference memory blocks, but only needs to increase flexibility and semantics. If a structure is a student's score, which contains 40 academic scores, the average score is calculated as follows:
Struct student
{
Float course1, course2 ,..., Course40;
Static float Student: * pcourse [40];
};
Float Student: * Student: pcourse [40] ={ & Student: course1 ,..., & Student: course40 };
You can write a loop to simplify computing, for example:
Student A; float AVG = 0.0f;
For (long I = 0; I <40; I ++) AVG + = A. * Student: pcourse [I];
AVG/= 40366f;
The same is true for member function pointers. You can understand the use of various pointers only according to the aforementioned rules and semantics.
Reference memory blockThis is the original semantics of the pointer. When you want to transmit a memory block, it can only be achieved by passing the pointer (the memory block cannot be passed), for example:
Bool getpersoninfo (personinfo * pinfo );
Personinfo temp; getpersoninfo (& temp );
// Use the returned personal information in temp
The above parameter pinfo is used to pass a memory block to the getpersoninfo function so that it can fill in the personal information in the given memory block for transmission. If personinfo * getpersoninfo ();, the memory block referenced by the returned pointer needs to be released, but the code using getpersoninfo cannot know how the memory block is allocated and cannot be released, this is part of memory management, which is not discussed in detail here. Another example is:
Bool getmaxandmin (long [10], long * max, long * min );
Long a [10] = {5, 3, 10}, Max, min; getmaxandmin (A, & MAX, & min );
Reference NumberThis is a second-level indirect application formed by the indirect use of address-type numbers. The purpose is to transfer the pointer when the referenced number is larger than the pointer size to reduce the transfer cost. The preceding long [10] is 40 bytes. if the pointer is passed, only 4 bytes are allowed, greatly reducing the cost as follows:
Bool getmaxandmin (long (*) [10], long *, long *);
Long a [10] = {5, 3, 10}, Max, min; getmaxandmin (& A, & MAX, & min );
Here, the transfer of array a is much more efficient than before. Another example is the previous personinfo * getpersoninfo (); instead of personinfo getpersoninfo (); it is also out of sizeof (personinfo *) <sizeof (personinfo), so it is stupid: char max (char *, char *, char *);. Here we want to return the maximum value, but sizeof (char *)> sizeof (char) not only reduces the efficiency but also reduces the efficiency (for 32-bit compilers, this will not be due to lower transfer efficiency, but indirectly referencing numbers to reduce efficiency ).
Attention should be paid to the fact that the pointer can reference a number because of the second-level indirect, which leads to a second-level unnecessary reference-the reference to the memory block, this only wants to pass a number, as a result, the memory block of the number is also transmitted, which may cause problems. For example, in the previous getmaxandmin (& A, & MAX, & min);, after execution, the content of array A may be changed because the memory block of record A is also passed, the getmaxandmin function can arbitrarily modify the content of the memory block passed in, that is, the content of array. When the getmaxandmin function and the code that calls it are not compiled by the same person, the former can destroy the content of A and cause problems in the latter. This is called a vulnerability. Therefore, when designing external interfaces, you should be careful to determine whether the parameter type should be pointer type (note that for external interfaces, even if it is written as bool getmaxandmin (const long (*) [10], long *, long *); is meaningless ).
Semantic requirementsThe pointer syntax is reference. When the logic needs to be referenced, the pointer is used for ing. For example, the linked list node has a reference to the next node, therefore, when designing the knot of the linked list, it should have a member of the pointer type.
For function pointers, a function is a program. A program is a description of a method, that is, a function pointer is a reference to a method. For example, a container class has a reference to a sorting method, so that different sorting methods can be used to achieve different sorting (this is an "indirect" increase in flexibility ), therefore, the container class should have a member variable of the function pointer type.
In addition, the function pointer can implement callback. For example, when you search data and call the function search, there is a parameter that specifies what to do when the search time times out. Therefore, the search requires a function reference (the Code cannot be passed ), that is, the function pointer, as follows:
Bool bcontinue (unsigned long overseconds)
{
If (/* determine whether to continue */)
{/* Necessary processing */return true ;}
Return false;
}
Bool search (bool (*) (unsigned long),/* Other parameters */);
Search (& bcontinue,/* Other parameters */);
In the search above, you can call bcontinue at a certain time to ask if you want to continue searching to give a way to terminate a long search.
For member pointers, it is also a reference to Members. As stated in C ++ from scratch (12), member variables are the attributes and statuses of corresponding resources, and member functions are the functions of corresponding resources, the member pointer is the reference of the attributes and status of the resource and the reference of the Resource function. For example, a dog has three functions: wire walking, Fire Circle jumping, and inverted. One method is to perform a circus. To increase flexibility, you should keep a reference to the functionality of the dog in the circus function and perform different circus performances in different venues.
As can be seen from the above, the so-called semantic needs should be designed based on the indirect benefits mentioned above, and then show the need for reference in terms of semantics. But indirectly, it also has a disadvantage, that is, it may fail, but it will always succeed directly. The "original means" must be successful (because it is achieved directly, and its effectiveness should be considered if it is indirect at multiple levels). The failure is because the "advanced means" control fails. The reason why "Advanced means" fails to be controlled is that its configuration information is invalid, that is, the reference is invalid. For pointers, you must ensure that their values are valid. If they are invalid, memory access violations may occur. If no proper action is taken, the program may crash. Therefore, most users first check its validity before using pointers, the method of detection is to determine whether the memory identified by the referenced address is accessible currently (the operating system generally provides such an interface), but the efficiency of the operating system is low, generally, it is valid as long as it is not zero, so it is generally as follows:
If (p) {/* Use Pointer p */}
Therefore, the pointer is usually initialized to 0, such as long * p = 0 ;. Generally, a macro or constant is created to express this semantics. In MFC, the macro null is defined to represent 0, and long * P = NULL ;, null indicates null.
So far, we have explained the pointer and its application. The application of the pointer is based on the indirect, and the indirect advantages and disadvantages are clarified, which will help to better use the pointer.