Teach you to understand complicated C / C declaration
Original: http: //www.codeproject.com/cpp/complex_declarations.asp Author: Vikram A Punathambekar
Introduction
I have encountered it to make you confused, similar to INT * (* (* (* fp1) (int)) [10]; Is this variable declaration? This article will be easily difficult, step by step, how do you understand this complex C / C declaration: We will start from a simple statement you can encounter every day, then gradually add const modifiers and typedef, and function pointer, Finally, an "right left definition" that allows you to accurately understand any C / C declaration. It is necessary to emphasize that complex C / C declarations are not a good programming style; I just teach you how to understand these statements. Note: To ensure that code and related comments can be displayed on the same line, this paper is preferably read on a display of at least 1024x768 resolution.
basis
Let us start from a very simple example, as follows:
Int n;
This should be understood as "declare n as an int" (n is an INT type variable).
Let's take a look at the pointer variables as follows:
INT * P;
This should be understood as "declare p as an int *" (p is an int * type variable), or P is a pointer to an INT type variable. I want to discuss here: I think it is best to write * (or &) before the variable is declared in a statement of a pointer (or reference) type, not followed by the basic type. This will avoid some misunderstandings, such as:
INT * P, Q;
The first look, it seems that P and Q are int * type, but in fact, only P is a pointer, and Q is the simplest INT variable.
We still continue our topic, then look at the example of a pointer:
Char ** argv;
In theory, there is no restriction on the level of the pointer, you can define a pointer of the pointer of the pointer of a floating point type variable ...
Let's see the following statement:
INT rollnum [30] [4]; int (* p) [4] = rollnum; int * q [5];
Here, P is declared as a pointer to a 4 element (INT type) array, and Q is declared as an array containing five elements (INT type pointers).
In addition, we can also mix practical * and &, 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 reason is Illegal .int && p4; // error: Reference to a reference is Illegal.
Note: P1 is a pointer for an int type pointer; P2 is a reference to an int type pointer; P3 is a pointer (not legal!); P4 is a reference to an int type reference (not legal!).
Const modifier
When you want to block a variable, you may use the const keyword. While you have a variable plus const modifiers, you usually need to initialize it, because you will have no chance to change it at any time. For example: const INT n = 5; int const m = 10;
The above two variables n and m are actually the same type - all const INTs. Because the C standard specifies, the const keyword is equivalent to the type or variable name. I personally prefer the first declaration method because it highlights the role of const modifiers.
It is easy for people to be confused when Const is working with a pointer. For example, let's take a look at the following P and Q:
Const Int * P; int const * q;
Which of them represents a constant type pointer (Const Directly Modified Int), which one represents an int type const pointer (Const Direct Function Pointer)? In fact, P and Q are declared as a constent Int type pointer. The int type Const pointer should declare this:
INT * const r = & n; // n Has been declared as an int
Here, both P and Q are pointers to the const Int type, that is, you cannot change the value of * p in the later program. R is a const pointer, which is initialized to the variable N (ie R = & n;) after the declaration, the value of R will no longer be changed (but the value of * R can be changed).
In combination of the above two const modifications, 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 completely clarify the use of consts. However, please note that some of the following statements cannot be compiled because they need to initialize while declaring. For the sake of simplicity, I ignore the initialization section; because the initialization code is added, each of the following declarations will add two lines of code.
Char ** p1; // Pointer to P2; // Pointer to POINTER TO Const Charchar * const * p3; // Pointer to const p4; // Pointer to const P4; // Pointer To Const Pointer To const charchar ** const p5; // const power to pointer to charconst char ** const p6; // const Pointer to POINTER TO Const Charchar * const * const p7; // const Pointer to const Pointer to Charconst 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 pointer to the const char type; P3 is a const pointer to a CHAR type; P4 is a Const pointer to the const char type; P5 is a pointer to a CHAR type Const pointer; P6 is a Const pointer to the CONST CHAR type pointer; P7 is a const pointer to the CHAR type const pointer; P8 is a const pointer to the const char type Const pointer. Typedef
Typedef gives you a way to overcome "* only suitable for variables and is not suitable for type". You can use TypeDef as follows:
Typedef char * pchar; pchar p, q;
The P and Q here are declared as a pointer. (If you do not use Typedef, Q will be declared as a char variable, which is not consistent with our first eye!) There are some declarations using typedef and give an explanation:
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 // return a Pointer to a // function That Returns a // Pointer to a char
e var [10]; // var is an array of 10 Pointers to // Functions Returning Pointers to Chars.
Typedef is often used before a structural statement, as follows. This way, when creating a structural variable, allow you to use the keyword structure (in C, create a struct key when creating a structural variable, such as Struct TagPoint A; in C , struct can ignore, such as tagpoint b).
Typedef struct tagpoint {int x; int y;
Point P; / * Valid c code * /
Function pointer
Function pointers may be the most prone to understanding declarations. The function pointer is used up to the TSR program in the DOS time; in the Win32 and X-Windows era, they are used in the case where the callback function is required. Of course, there are many other places to use function pointer: virtual function table, some templates in STL, WIN NT / 2K / XP system service, etc. Let's take a simple example of a function pointer:
INT (* P) (char); here P is declared as a function pointer, this function with a char type parameter, and has an int type return value. In addition, there are two FLOAT type parameters, the return value is a function pointer of the pointer of the CHAR type pointer can be declared as follows:
CHAR ** (* P) (Float, Float);
Then, how does the constit pointer parameters with two char types, how do you declare the function pointer without reference? Reference is as follows:
Void * (* a [5]) (CHAR * Const, Char * Const);
"Right left law" [important! ! ! ]
The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left When you encounter parentheses, the direction should be reversed Once everything in the parentheses has been parsed, jump out of it Continue till... The Whole Declaration Has Been Pass.
This is a simple rule, but you can make you accurately understand all the statements. This method is used as follows: From the most internal brackets, read the statement, look at it right, then look left left. When you encounter a parentheses, you will turn the direction of reading. All the contents in parentheses are analyzed and jumped out of the brackets. This continues until the entire statement is analyzed.
Do a small correction for the above "right left law": When you start reading the statement for the first time, you must start from the variable name, not from the inside of parentheses.
The following combine example to demonstrate the use of "right left default".
INT * (* (* fp1) (int)) [10];
Reading steps: 1. Starting from the variable name --------------------------------------- --- FP12. See the right, nothing, I have encountered it), so see it, I encountered a * ------ a pointer 3. Jump out of the parentheses, I have encountered (int) -------- ---------------------------- A function with an int parameter 4. Look left, find one * ----- ---------------------------------- (function) Returns a pointer 5. Jump out of parentheses, look at it right, touch To [10] ------------------------------ An array of 10 elements 6. Look left, find one * - ------------------------------------- Pointer 7. Look left, find int ---- ------------------------------------- Int type
Summary: The fp1 is declared as an array of pointers of a function of a function. This array has 10 elements. The prototype of the function is a parameter with an int type. The return value is a pointer?
Let's take another example:
INT * (* Arr [5]) ()) ();
Reading steps: 1. Starting from the variable name --------------------------------------- --- Arr2. Look at the right, discovery is an array -------------------------------- a 5 element Array 3. Look left, find one * --------------------------------------- Pointer 4. Jump out of parentheses, look to right, discovery () ---------------------------- no parameters 5. Look left, touch * --------------------------------------- - (function) Returns a pointer 6. Jump out of parentheses, discovery to right () ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Function without parameters 7. To the left, discovery * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ----------- (function) Returns a pointer 8. Continue to the left, discovery int ----------------------- -------------- INT type summary:? ?
There are more examples:
Float (* (* b ()) []) (); // b is a function That Returns a // Pointer to Functions Returning floats.
Void * (* c) (CHAR, INT (*)); // c is a pointer to a function Takes // Two parameters: // a char and a pointer to a // function 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 Takes // Two parameters: // a reason to an an Int And a pointer // to a function this takes two parameters: // a Pointer to a one of the a char // and returns a pointer to a pointer // to a char // and returns a Pointer to a pointer to voidfloat (* (* e [10]) (int &)) [5]; // e is an array of 10 Pointers to // Functions Take a single // Reference to an Int as an argument // and return Pointer S to // AN array of 5 floats.