Go: How to understand complex type declarations for C and C + +

Source: Internet
Author: User

The author Girlrong is the C-language moderator of the NetEase Guangzhou community, and this article was selected in the elite area. is very good, dare not to enjoy alone! It is said that she is willing to help others, modest and sincere, quite popular with netizens. It is a pity that we have retired to the lake. In the recent study of the C language process, understand some of the experience of elder Daniel

Have you ever come across something that puzzles you, like an int * (* (*FP1) (int)) [10]; A variable declaration like this?

This article will teach you how to understand this complex, C + + declaration from easy to difficult, step-by-step.

We'll start with a simpler statement that we can run into every day, and then step into the const modifier and typedef, as well as the function pointers, and finally introduce a right-to-left rule that allows you to accurately understand any C + + declaration.

It should be emphasized that complex C/s + + declarations are not a good programming style; I'm just here to teach you how to understand these statements. Note: In order to be able to display code and related comments on the same line, this article is best read on a display with at least 1024x768 resolution.

Let's start with a very simple example, as follows:

int n;

This should be understood as "declare n as an int" (n is a variable of type int). Then take a look at the pointer variable, as follows:

int *p;

This should be understood as "declare p as an int *" (p is a variable of int *), or P is a pointer to a variable of type int. I'd like to discuss this here: I think when declaring a variable of a pointer (or reference) type, it's better to write * (or &) before the variable, rather than following the base type. This avoids some misunderstanding of understanding, such as:

Let's look at an example of a pointer pointer:

Char **argv;

Theoretically, there is no limit to the progression of pointers, you can define a pointer to a pointer pointer to a floating-point type variable, and then see the following declaration:

int rollnum[30][4];

int (*p) [4]=rollnum;

int *q[5];

Here, P is declared as a pointer to an array of 4 elements (int type), and Q is declared as an array containing 5 elements (int type). In addition, we can also mix utility * and &amp in the same declaration, as follows:

int **p1;

P1 is a pointer to a pointer to an int.

int *&p2;

P2 is a reference to a pointer to an int.

int &*p3;

Error:pointer to a reference is illegal.

int &&p4;

Error:reference to a Reference is illegal.

Note: P1 is a pointer to a pointer of type int; P2 is a reference to a pointer of type int; P3 is a pointer to an int type reference (illegal!). P4 is a reference to an int type reference (illegal!). )。



Const modifier

The const keyword may be used when you want to prevent a variable from being changed. When you add a const modifier to a variable, you usually need to initialize it, because at any later time you will not have a chance to change it. For example:

const int n=5;

int const M=10;

The two variables n and M are actually the same type--both const int (shaping constants). Because the C + + standard stipulates that the Const keyword is placed before the type or variable name is equivalent. I personally prefer the first way of declaring because it highlights the role of the Const modifier. When const is used with pointers, it is easy to confuse people. For example, let's take a look at the following p and Q declarations:

const int *p;

int const *Q;

Which one of them represents a const pointer of type INT (const directly modifies int), which represents a const pointer of type int (const direct modifier pointer)? In fact, both P and Q are declared as pointers to the const int type. The const pointer of type int should be declared like this:

int * Const r= &n;

n have been declared as an int

Here, p and Q are pointers to the const int type, which means that you cannot change the value of *p in a later program. R is a const pointer that, when declared, is initialized to the variable n (that is, r=&n;), and the value of R is no longer allowed to be changed (but the value of *r can be changed).



Combining the two const modifiers above, let's declare a const pointer to the const int type, as follows:

const INT * Const P=&N

N has been declared as const int



Some of the statements given below about const will help you to completely clarify the usage of Const. Note, however, that some of the following declarations cannot be compiled because they need to be initialized at the same time as they are declared. For the sake of brevity, I ignored the initialization section, and each of the following declarations adds two lines of code, because of the addition of the initialization code.

char * * p1;

Pointer to Pointer to Char

const char **P2;

Pointer to pointer to const CHAR

char * const * P3;

Pointer to const pointer to Char

const char * const * P4;

Pointer to const pointer to const CHAR

char * * Const P5;

const pointer to pointer to Char

const char * * Const P6;

const pointer to pointer to const CHAR

char * const * Const P7;

const pointer to const pointer to Char

const char * const * Const P8;

const pointer to const pointer to const CHAR



Note: P1 is a pointer to a pointer to a char type, p2 is a pointer to a const char type pointer, p3 is a const pointer to a char type, P4 is a const pointer to a const char type; P5 is a const pointer to a pointer to a char type ; P6 is a const pointer to a pointer to a const char type; P7 is a const pointer to a char type const pointer; P8 is a const pointer to a const pointer to a const char type.



The Magical magic of typedef

typedef gives you a way to overcome the drawbacks of "* only appropriate for variables and not suitable for types". You can use typedef as follows:

typedef char * PCHAR;

PCHAR p,q;

Both P and Q are declared as pointers. (If you do not use TYPEDEF,Q will be declared as a char variable, this is not quite consistent with our first glance!) Below are some declarations that use TypeDef, and give explanations:

typedef char * A;

A is a pointer to a char

typedef a B ();

B is a function that returns

A pointer to a char

typedef b *C;

c is a pointer to a function

That returns a pointer to a char

typedef c d ();

D is a function returning

A pointer to a function

That returns a pointer to a char

typedef d *E;

E is a pointer to a function

Returning a pointer to a

function that returns a

Pointer to a Char

e Var[10];

var is an array of ten pointers to

Functions returning pointers to

Functions returning pointers to chars.



typedef are often used before a struct declaration, as follows. This allows you to create struct variables without using the keyword struct (in c, the struct keyword is required when creating struct variables, such as struct tagpoint A; in C + +, structs can be ignored, such as Tagpoint b).

typedef struct TAGPOINT

{

int x;

int y;

}point;



Point P;



function pointers

A function pointer may be the most confusing statement that can cause comprehension. Function pointers are used most when writing TSR programs in the DOS era, and in the Win32 and x-windows times, they are used where callback functions are needed. Of course, there are many other places that need to use function pointers: virtual function tables, some templates in STL, Win NT/2K/XP System services, and so on. Let's look at a simple example of a function pointer:

Int (*p) (char);

Here p is declared as a function pointer, which takes a char type parameter and has a return value of type int. In addition, a function pointer with two float type parameters, a pointer to a pointer of type char, can be declared as follows:

char * * (*p) (float, float);

What about a const pointer parameter with two char types and a function pointer with no return value? Refer to the following:

void * (*a[5]) (char * const, char * const);

The "right left law" is a simple rule, but it allows you to understand all the statements accurately. This rule is used as follows: Start reading the declaration from the inner parenthesis, look right, and then look left. When you touch a parenthesis, you turn the direction of reading. All the contents of the parentheses are parsed out of the range of parentheses. This continues until the entire statement has been parsed.



Make a minor correction to the right-left rule: When you first start reading the declaration, you have to start with the variable name instead of the inner parenthesis.

The following example illustrates the use of the right-left rule.

int * (* (*FP1) (int)) [10];

Read the steps:

1. Start with the variable name--FP1

2. Look to the right, nothing, bump into it, so look left, touch a *--a pointer

3. Jump out of parentheses, hit (int)--a function with an int argument

4. Looking left, a *--(function) is found to return a pointer

5. Jump out of brackets, look right, touch [10]--An array of 10 elements

6. Look left and find a *--pointer

7. Look left, find Int--int type

Summary: FP1 is declared as a pointer to a function that returns a pointer to an array of pointers.



Let's look at one more example:

int * (* (*arr[5]) ()) ();

Read the steps:

1. Start with the variable name--arr

2. Look to the right and find an array--an array of 5 elements

3. Look left and find a *--pointer

4. Jump out of brackets, look right, find ()--function without parameters

5. Look left, Touch *--(function) returns a pointer

6. Jump out of parentheses to the right to find ()--function without parameters

7. Left, find *--(function) returns a pointer

8. Continue to the left and find the Int--int type



There are a number of other examples:

Float (* (*b ()) []) ();

B is a function that returns a

Pointer to an array of pointers

to functions returning floats.

void * (*C) (char, int (*) ());

c is a pointer to a function that takes

Parameters:

A char and a pointer to a

function that takes no

Parameters and returns

an int

and returns a pointer to void.

void * * (*d) (int &,

char * * (*) (char *, char * *));

D is a pointer to a function that takes

Parameters:

A reference to an int and a pointer

To a function that takes the parameters:

A pointer to a char and a pointer

To a pointer to a char

and returns a pointer to a pointer

to a Char

and returns a pointer to a pointer to void

Float (* (* e[10])

(int &)) [5];

E is an array of ten pointers to

Functions a single

reference to a int as an argument

and return pointers to

An array of 5 floats.



Add:

All complex pointer declarations in C are made up of a variety of declaration nesting. How to interpret complex pointer declarations? Right-left law is a well-known and commonly used method. However, the right-to-left law is not the content of the C standard, it is from the C standard of the Declaration of the method summed up. The C Standard's declaration rules are used to solve how to create a claim, and the right-hand rule is used to resolve how to identify a declaration, which can be said to be the opposite. The English text of the right-to-left law is said:

The Right-left Rule:start reading the declaration from the innermost parentheses, go right, and then go to left. When you encounter parentheses, the direction should is reversed. Once everything in the parentheses have been parsed, jump out of it. Continue till the whole declaration has been parsed.


This English translation is as follows:

Right-to-left rule: First look at the innermost parentheses, then look right and look left. Whenever you encounter parentheses, you should reverse the direction of reading. Once you have finished parsing all the parentheses inside, jump out of the parentheses. Repeat this process until the entire declaration is resolved.

I would like to make a small amendment to this rule, should be not defined by the identifier to start reading, rather than from the parentheses read, the reason is undefined identifier, because there may be multiple identifiers in a declaration, but the undefined identifier will only have one.

Now, with some examples to discuss the application of right-to-left law, let's start with the simplest, and gradually deepen:

Int (*func) (int *p);

First find the undefined identifier, is the Func, it has a pair of parentheses outside, and the left is a * number, which means that func is a pointer, and then jump out of this parenthesis, first look to the right, is also a parenthesis, which means (*func) is a function, The func is a pointer to such a function, which is a function pointer, which has a parameter of type int*, and the return value type is int.

Int (*func) (int *p, int (*f) (int*));

Func is enclosed by a pair of parentheses and has an * sign on the left, stating that Func is a pointer, jumping out of parentheses, and having a parenthesis on the right, then Func is a pointer to a function that has a formal parameter such as int * and INT (*) (int*), and the return value is of type int. Then take a look at the func parameter int (*f) (int*), similar to the previous explanation, F is also a function pointer, the function that points to has a int* type of formal parameter, the return value is int.

Int (*func[5]) (int *p);

The right side of the Func is a [] operator, stating that Func is an array with 5 elements, and the left side of Func has a *, indicating that the element of Func is a pointer, and that this is not a modifier of func, but a modification of func[5], because the [] operator has precedence over *, and Func is preceded [] Combined, so the * modifier is func[5]. Jumping out of this parenthesis, looking to the right, is also a pair of parentheses, stating that the element of the Func array is a pointer to the function type, the function it points to has a int* type parameter, and the return value type is int.


Int (* (*FUNC) [5]) (int *p);

Func is enclosed by a parenthesis, and there is another * on the left, then Func is a pointer, jumping out of parentheses, to the right is a [] operation symbol, indicating that func is a pointer to an array, and now to the left, there is an * number on the left, indicating that the element of this array is a pointer, and then a parenthesis, Indicates that the element of this array is a pointer to a function. To sum up, that is: Func is a pointer to an array whose elements are function pointers to functions that have int* parameters and return values of type int.

Int (* (*func) (int *p)) [5];

Func is a function pointer that has a formal parameter of type int*, and the return value is a pointer to an array, and the element of the array that is pointed to is an array with 5 int elements.

It is important to note that some complex pointer declarations are illegal, such as:

int func (void) [5];

Func is a function that returns a value that has an array of 5 int elements. However, the function return value of the C language cannot be an array, because if the function returns the value array, then the object that receives the contents of the array must also be a valarray, but the C-language array name is an rvalue, and it cannot be an lvalue to receive another array, so the function return value cannot be array.

int func[5] (void);

Func is an array of 5 elements, and the elements of this array are functions. This is also illegal, because the elements of an array must be the same in addition to the type of memory that each element occupies, and obviously the function cannot achieve this requirement, even if the function is of the same type, but the space occupied by the function is usually not the same.

As an exercise, here are a few complex pointers to the reader's own interpretation, and the answer is in the tenth chapter.

Int (* (*FUNC) [5][6]) [7][8];

Int (* (* (*FUNC) (int *)) [5]) (int *);

Int (* (*func[7][8][9]) (int*)) [5];

In practice, when a complex pointer needs to be declared, it is a major detriment to the readability of the program if the entire declaration is written in the form shown above. A typedef should be used to decompose the declaration layer by level to enhance readability, for example, for declarations:

Int (* (*func) (int *p)) [5];

Can be decomposed like this:

typedef int (*para) [5];
typedef PARA (*FUNC) (int *);

This makes it easier to see.

Go: How to understand complex type declarations for C and C + +

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.