Reprinted from: http://soft.chinabyte.com/database/248/12193748.shtml
"C Expert Programming" chapter 3rd study
The statement in C is a headache, especially if you are a beginner, and you will be afraid of the following formulas, even if you are a novice. Because it's too complicated for people who have just come into contact with this form, assuming you don't have the expertise to analyze it.
First declaration: void (*signal (int sig, void (*func)) (int)), second declaration: const Char *const (*sinc (char *SINCG (), int (*p) (int a,int *b) ) (int **sing), where the first declaration is a declaration of a system function, and the second one is my own improvisation, but I can assure that it has no drawbacks other than the complexity. If you're having a headache with these two statements, and are anxious to know how to analyze such statements to improve your C level and proficiency. So please read the following and let me share what I have learned with you!
First of all, let's look at a very simple example-complex things are made up of simple, once all the simple are understood, you can, and also easily understand the complex problems, and then only need a turn on the line, the turnaround is often summed up.
Char next; it is easy to know that a variable of type char next is declared here. Next here is an identifier that indicates its variable identity, so we can think of that once the identifier, like next, is replaced with such a word, (identifier) is ..., For example, the previous next, you can use Next is ... Instead, like the macro definition in C, if that makes you feel a bit dizzy, let's just define it in a different way, #define identifier identifier is ...
In other words, once the identifier is encountered, your mind immediately uses (identifier) is ... Such a word to completely replace that identifier. So, the statement just now, Char next; we think about it in a more canonical, mathematically-like form of thinking, first we analyze the identifier next, and according to the previous statement, we see next and immediately use next. Instead, we get the first part of the entire declaration: "Next is ... "Such a sentence, but what is next, when we need to look at other things in the declaration, such as the char in the example above, it is natural to make it clear that next is (a variable of type char). So char indicates a type variable. So, after such a process of thinking, the above statement is completed analysis.
Let's not lose patience, I'm going to use such a long space to describe a simple example that is not to lose your appetite, but rather to make it easier for you to understand the content below. Making my text jump doesn't make your mind feel abrupt or even short-circuited-that's not what I'm writing this summary for.
Next we make this statement a little bit longer, making it char next (), or Char next[]; At this time, according to what we have learned, it is still easy to see that the previous one is declared to return char type variable function, and the latter is to declare a char class The array of type variables, it is this time, there is a question please notice, we add this parenthesis after what really makes our thinking change what, why when the two symbols appear in the We will understand that this is a function or an array, so, Here I want to tell you a new point of knowledge that you may never have realized is that the parentheses and brackets are the highest priority in the declaration [/b]. This is a very important point of knowledge when you find the right side of the identifier next to the left parenthesis (please note that I am writing the left parenthesis [/b]) or square brackets, you do not have any doubts to tell yourself that the identifier is a (return ... function) or an array. Let's use this kind of thinking to analyze char next (); This statement first parses the identifier next and concludes that next is .... , and then to the right it's not the parenthesis or the square bracket, and the example is a parenthesis, so we get Next is a return .... function, and finally from the char type, next is a function that returns a char variable, and the entire declaration is parsed.
OK, now let's summarize the first two basic steps in the statement analysis, which are also the key steps.
First, the identifier in the analysis declaration, there is a problem, I do not know that we found that the example above is only one identifier, if there are multiple identifiers in the declaration how to handle, for example, add a few parameters in the next function, such as char next (int a,int b); There are three identifiers in the entire declaration, and we choose which identifier to parse first. With this simple example, it is easy to see that the first choice is the leftmost identifier, and of course, we must not draw a general conclusion on the basis of a special case, where I tell you, in the capacity of a person who has mastered this knowledge, that your conjecture is correct, that the identifier is handled from the leftmost, That's exactly what C does. So now, you've mastered this point of knowledge, so let's move on to the discussion. When we select an identifier, we look at the declarator immediately to its right (that is, the various symbols and variables appearing in the Declaration, such as (), *,const,[], etc.), and there are two cases: if a left parenthesis is present, then the identifier is a return .... function, if there is a square bracket, there is no doubt that the identifier is an array, as to what kind of array, you must pass the analysis to be able to know.
Well, now there's a new problem, that is, if the immediate right side of the identifier is neither the left parenthesis nor the square bracket? A right parenthesis, for example. This requires our third, more important, analysis of the steps of the Declaration. That is: Look at the symbol to the left of the identifier, divided into the following two kinds: A, if the left side of the identifier is a left parenthesis, then find and it matches the right parenthesis, the entire parentheses in the Declaration as a whole analysis.
B, if the left side of the identifier is either *, or const, or one of the volatile three, continue to look to the left until the declarator exceeds three. That is to say, always find a symbol that is neither *,const nor volatile.
C, after B, if the symbol is a left parenthesis, return to a for processing.
Finally, the rest of the symbols can be read together, because that must already be very easy to understand.
Here, I want to explain to the novice friend Const and volatile two modifiers, const means that the modified variable is read-only, that is, once the assignment can no longer be modified. For example const char p; const char *p; It is important to note that the const char *p; and char * const p; is a different declaration, the previous one indicates that the pointer p points to the content is read-only, and the latter indicates that the pointer itself is read-only and that the content it points to can be changed. I summed up a rule for everyone to remember conveniently, that is, if the * and identifier is a whole, not separated by anything, it means that the const modifier is the pointer to the content, such as the const char *p in the previous example, or char const *p;* and identifiers (p) Without being separated by the Const modifier, the const modifier is what the pointer points to, and once the * and the identifiers are separated, the const modifier is the pointer itself, and the variable to which the pointer points is variable. Unless the pointed variable itself is also const-decorated, such as the const char * const p; the second modifier volatile indicates that the modified variable is that he can be changed by this program and other program data, like system time, whether this program is not a breakpoint, sleep, Other programs change his value.
Well, if you read the above section carefully, you will find that after careful analysis, the statement of understanding is not how difficult, now we first to use a more conventional statement to master the above way of thinking, and finally we solve the article began to mention the two declaration as the end.
Char (*P) ();//First Look at the leftmost identifier p, (indicates P is ...) His right hand is neither the left parenthesis nor the square bracket, so look to the left and find that *, according to the above principle (if the left side of the identifier is *, or const, or one of the volatile three, then continue to look to the left until the declarator exceeds three.) That is to say, always find a symbol that is neither *,const nor volatile. We continue to look to the left and find that the next symbol is (, so according to the principle (after B, if the symbol is the left parenthesis, then go back to a for processing.) We look for the right parenthesis that matches the left parenthesis, so we can handle the parentheses as a whole, and the parentheses in the example are *p, so we conclude that P is a pointer, and since it is a pointer, it must point to something, and this thing, We have to go on to dig it out, and now that we've got the bracketed thing as a sort of a collation, it's quite a variable (we've actually got a pointer-type variable by analyzing what's in the parentheses) so we can take it as the initial identifier, go back to the first step analysis, look at the variable (In fact, the entire bracket as an identifier, parsing the parenthesis closest to the right of the symbol) is adjacent to the left parenthesis or square brackets, fortunately, we found the left parenthesis, it means that the variable (that is, the entire parenthesis) is a return ... function, and the original equivalent of the parentheses inside the variable is actually a pointer variable, and then combined with what we have just analyzed, we can know that this pointer variable points to a return .... function, finally we analyze what this function returns, this time only a char type is left, so the whole declaration is a pointer to a function, the function returns a char type of variable analysis of such a declaration is not difficult, difficult to use the above thinking to analyze, The above analysis steps are actually the compiler's steps to declare the analysis, but in fact the above summary is not yet complete. Then I will list the complete statement of the analysis steps: The analysis of the steps to match the symbol reading Mode 1 take the leftmost identifier identifier indicates that the identifier is ...
2 View identifiers to the right of the next [possible size for each pair, representing the symbol, if it is square brackets ... An array of 3 if it is a left parenthesis (possible argument) to the right parenthesis so far as the content represents the return ... Function 4 If the symbol on the left is one (the left parenthesis combines the left parenthesis portion of the processed statement together until the corresponding closing parenthesis is met, and then restarts from the second step 5 if the symbol on the left is one of the following symbols *,const,volatile *,const,volatile Continue reading the symbol to the left, knowing that the declarator is outside of the three, and then repeat step 4th 6 The Remaining symbols form basic type basic types such as char,int remaining symbols can be read together
The above is the declaration of the full summary version of the analysis, but also the C expert programming book listed out, I just learn from this, for everyone to learn reference.
Well, with the above basics, we'll be good at analyzing the first scary statement, from which the advantages of our analysis steps above are reflected
void (*signal (int sig, Void (*FUNC) (int)))) (int);
First, the leftmost identifier is signal, which indicates that signal is .... , close to the right of it is a left parenthesis, stating that signal is a return ... function, and the int sig, Void (*FUNC) (int) is the parameter of this function, each part of it can be re-analyzed with our method, and this is not described here. Then we follow the above analysis step, and then see what the symbol on the left is, is a pointer character *, indicating that the function is a function to return a pointer, at this time, the function has been simplified to void P (int), J where P is a function to return pointers, is the result of our analysis above. So what is the pointer pointing to, and here again, based on the previous steps, analyze the symbol on the right side of the entire parenthesis, that is, p (we have equal the contents of the entire parentheses to the imaginary identifier of p) to the right of the symbol, found to be a left parenthesis, so P is a return ... function, and P is a pointer (actually a pointer returned by a function), so p is a pointer to a function, and finally, judging by void, the return value of the function is null. In this way, a complex statement is cobwebs by our analysis, the full version of the language to describe the statement: This is a function, the function has two parameters (the argument of the Declaration of the analysis of the people to complete their own), and the function returns a pointer to a function, the function has a parameter, and the return type is empty.
Well, writing here, hoping that you've understood what I want to say and that I'm respecting you, I'll give you a second statement that looks more challenging. If there is any problem or find out what is wrong with this article, it is very welcome to discuss the group, but also very willing to make a good friend with you.
Let's study progress together.
Summary of the method of affirming and parsing in C language