7. Exploring the mysteries of C/C ++ arrays and pointers in depth: right-left rule-complex pointer Parsing

Source: Internet
Author: User

Explore the mysteries of C/C ++ arrays and pointers in depth 7: Right left law-complex pointer Parsing

First, let's take a look at the following statement:

Int * (* Fun) (int *) [10]; 
This is a function pointer statement that will make beginners feel dizzy and scared. It is difficult to understand such complex statements without learning certain rules Before mastering C/C ++ statement syntax.
C/C ++ all the complex declarative structures are composed of various declarative nesting. How to interpret complex pointer declarations? The right-left rule is a well-known and effective method. However, the right-left rule is not actually the content in the C/C ++ standard. It is a method summarized from the C/C ++ standard statement. C/C ++ standard declaration rules are used to solve how to create a declaration, while the right-left rule is used to identify a declaration. From the perspective of nesting, the two can be said to be the opposite process. The original English version of the right-left rule is as follows:
The right-left rule: start reading the declaration from the innermost parentheses, go right, and then go left. when you encounter parentheses, the direction shoshould be reversed. once everything in the parentheses has been parsed, jump out of it. continue till the whole declaration has been parsed.
The English translation is as follows:
Right left rule: first, start from the parentheses in the innermost part, and then look to the right, then to the left. When parentheses are encountered, the reading direction should be dropped. Once everything in the parentheses is parsed, the parentheses appear. Repeat this process until the entire declaration is parsed.
I want to make a small correction to this rule. It should be read from undefined identifiers rather than from parentheses. The reason is that there may be multiple identifiers in a declaration, but there is only one undefined identifier.
Now we can use some examples to discuss the application of the right-left rule, starting from the simplest:

INT (* func) (int * P ); 
First, find the undefined identifier, that is, func, which has a pair of parentheses and a * sign on the left. This indicates that func is a pointer and jumps out of the parentheses, first look at the right, it is also a parentheses, which means (* func) is a function, and func is a pointer to this type of function, is a function pointer, this type of function has an int * type parameter, and the return value type is int.

INT (* func) (int * P, INT (* f) (int *)); 
Func is enclosed by a pair of parentheses, and there is a "*" on the left, which indicates that func is a pointer. It jumps out of the brackets and there is a bracket on the right. Then func is a pointer to the function, this type of function has parameters such as int * and INT (*) (int *), and the return value is of the int type. Let's take a look at the func parameter int (* f) (int *). Similar to the previous explanation, F is also a function pointer. the pointer to a function has an int * type parameter and the return value is int.

INT (* func [5]) (int * P ); 
The right side of func is a [] Operator, indicating that func is an array with five elements, and there is a * on the left of func, indicating that the element of func is a pointer, note that * not modifying func, but modifying func [5] is because the [] operator has a higher priority than *, and func is first combined, therefore, * modifies func [5]. Jump out of this bracket and look at the right. It is also a pair of parentheses, indicating that the element of the func array is a pointer of the function type. It points to the function with an int * type parameter and the return value type is int.

INT (* func) [5]) (int * P ); 
Func is enclosed by parentheses, and there is a * on the left. func is a pointer, out of parentheses, and a [] operator number on the right, which indicates that func is a pointer to an array, to the left, there is a "*" on the left, indicating that the element of this array is a pointer, and then the brackets appear on the right, indicating that the element of this array is a pointer to the function. To sum up, func is a pointer to an array. The elements of this array are function pointers. These pointers point to functions with int x parameters and return values of the int type.

INT (* func) (int * p) [5]; 
Func is a function pointer. This type of function has an int * type parameter. The returned value is a pointer to an array. The elements of the array to which it points are arrays with five int elements.
Note that some complex pointer declarations are invalid, for example:

Int func (void) [5]; 
Func is a function that returns an array with five int elements. But the function return value of C language cannot be an array. This is because if the function return value is an array, what receives the content of this array must also be an array, however, the array name in C/C ++ is an unchangeable left value and cannot be directly modified by the content of another array. Therefore, the return value of the function cannot be an array.

Int func [5] (void ); 
Func is an array with five elements. All elements of this array are functions. This is also invalid because the array element must be an object, but the function is not an object and cannot be an array element.
In actual programming, if you need to declare a complex pointer, if you write the entire declaration as shown above, it will bring some damage to readability, you should use typedef to layer-by-layer decomposition of the Declaration, enhance readability.
Typedef is a declaration, but it does not declare a variable or create a new type, but a type alias. Typedef has a great purpose. It is one of its functions to break down a complex declaration to enhance readability. For example, for declaration:

INT (* func) (int * p) [5]; 
It can be decomposed as follows:

Typedef int (* para) [5]; </P> <p> typedef para (* func) (int *); 
This makes it easier to see.
Another role of typedef is as a high-level abstraction method based on object programming. In ADT, it can be used to establish associations between C/C ++ and objects in the real world and abstract these objects into C/C ++ type systems. When designing ADT, we often declare the alias of a pointer, for example:

Typedef struct node * List; 
From the ADT perspective, this statement is a natural thing. You can use list to define a list. However, from the perspective of C/C ++ syntax, it does not conform to the logic of C/C ++ declaration syntax, it violently separates the pointer declarative from the pointer declarative, which may lead to a phenomenon different from people's reading habits. Consider the following code:

Const struct node * P1; </P> <p> typedef struct node * List; </P> <p> const list P2; 
The P1 type is const struct node *. What about P2? If you think that the list is simply "substituted" p2, And the P2 type is also the result of const struct node *, it is a big mistake. The type of P2 is actually struct node * const P2, which is limited to P2, not node. The cause of this singular phenomenon is that the pointer declarative is split, as specified in the standard:
6.7.5.1 pointer declarators
Semantics
If in the Declaration 'td1', D1 has the form
* Type-qualifier-listopt d
And the type specified for ident in the Declaration 'td' is
''Derived-declarator-type-list t''
Then the type specified for ident is
''Derived-declarator-type-list type-qualifier-list pointer to t''
For each type qualifier in the list, ident is a so-qualified pointer.
The pointer declarative is composed of the pointer declarative *, the optional type qualified word type-qualifier-listopt, And the identifier D, which are logically integral, constitute a complete pointer declarative. This is also the reason why the pointer declaration must keep up with the identifier when multiple variables are defined in the same column. For example:

Int * p, q, * K; 
P and K are pointers, but Q is not. This is because * P and * k are an overall pointer declarative to indicate a pointer. The compiler will include the type on the left of the pointer declaration operator as the object type pointed to by the pointer, and the qualified word on the right is the declared identifier. But now, the typedef struct node * List separates * from the entire pointer declarative. If the compiler cannot find *, the const In the const list P2 is qualified for P2, the type of P2 is node * const rather than const node *.
Although typedef struct node * List does not conform to the declaration syntax logic, we should allow users to use List A in this way based on the important role of typedef in ADT and the requirements of information hiding, instead of list * a, the above typedef syntax should still be used in the ADT design, but the adverse effects should be noted.
Original article: http://blog.csdn.net/supermegaboy/archive/2009/11/23/4854965.aspx.

Related Article

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.