|
So this week's lecture was kind of a dud. In the future I'll
know to have more thorough lecture notes, to throw things at Antonio
and Heather, and not to try to do class right after hearing some
alum read lists for two hours.
To give you a better background for the lab, I've excerpted from
Peter van der Linden's Expert
C Programming:
The Precedence Rule
We have now reviewed the building blocks of declarations. This section
describes one method for breaking them down into an English
explanation. The precedence rule for understanding C declarations is
the one that the language lawyers like best. It's high on brevity, but
very low on intuition.
The Precedence Rule for Understanding C Declarations
A -- Declarations are read by starting with the name and then reading in precedence order.
B -- The precedence, from high to low, is:
B.1 -- parentheses grouping together parts of a declaration
B.2 -- the postfix operators:
parentheses ( ) indicating a function, and
square brackets [ ] indicating an array.
B.3 -- the prefix operator: the asterisk denoting "pointer to".
C -- If a const and/or volatile keyword is next to a type specifier (e.g. int, long, etc.) it applies to the type specifier. Otherwise the const and/or volatile keyword applies to the pointer asterisk on its immediate left.
Solving a Declaration Using the Precedence Rule
An example of solving a declaration using the Precedence Rule:
char * const *(*next)();
(Rule to apply followed by explanation)
A -- First, go to the variable name, "next", and note that it is
directly enclosed by parentheses.
B.1 -- So we group it with what else is in the parentheses, to get "next is a pointer to..."
B -- Then we go outside the parentheses, and have a choice of a prefix
asterisk, or a postfix pair of parentheses.
B.2 -- Rule B.2 tells us the highest precedence thing is the function parentheses at the right, so we have "next is a pointer to a function returning-"
B.3 -- Then process the prefix "*" to get "pointer to".
C -- Finally, take the "char * const", as a constant pointer to a
character.
Then put it all together to read:
"next is a pointer to a function returning a pointer to a const pointer-to-char"
and we're done. The precedence rule is what all the rules boil down to,
but if you prefer something a little more intuitive, click here.
Unscrambling C Declarations by Diagram
In this section we present a diagram with numbered steps (see
Figure3-3). If you proceed in steps, starting at one and following the
guide arrows, a C declaration of arbitrary complexity can quickly be
translated into English (also of arbitrary complexity). We'll simplify
declarations by ignoring typedefs in the diagram. To read a typedef,
translate the declaration ignoring the word "typedef". If it translates
to "p is a-", you can now use the name "p" whenever you want to declare
something of the type to which it translates.
Let's try a couple of examples of unscrambling a declaration using the
diagram. Say we want to figure out what our first example of code
means:
char * const *(*next)();
As we unscramble this declaration, we gradually "white out" the pieces
of it that we have already dealt with, so that we can see exactly how
much remains. Again, remember const means "read-only". Just because it
says constant, it doesn't necessarily mean constant.
The process is represented in the table below. In each step, the portion
of the declaration we are dealing with is printed in bold type.
Starting at step one, we will proceed through these steps.
Steps in Unscrambling a C Declaration
Declaration Remaining (start at leftmost identifier) |
Next Step to Apply |
Result |
| char * const *(*next) (); |
step 1 |
say "next is a ..." |
| char * const *(*_ _ _ _ ) (); |
step 2, 3 |
doesn't match, go to next step, say "next is a ..." |
| char * const *(*_ _ _ _ ) (); |
step 4 |
doesn't match, go to next step |
| char * const *(*_ _ _ _ ) (); |
step 5 |
asterisk matches, say "pointer to ...", go to step 4 |
| char * const *(_ _ _ _ _) (); |
step 4 |
"(" matches up to ")", go to step 2 |
| char * const * _ _ _ _ _ _ _ (); |
step 2 |
doesn't match, go to next step |
| char * const * _ _ _ _ _ _ (); |
step 3 |
say "function returning ..." |
| char * const * _ _ _ _ _ _ _ _ _; |
step 4 |
doesn't match, go to next step |
| char * const * _ _ _ _ _ _ _ _ _ ; |
step 5 |
say "pointer to ..." |
| char * const _ _ _ _ _ _ _ _ _; |
step 5 |
say "read-only ..." |
| char *_ _ _ _ _ _ _ _ _ _ _ _ _ ; |
step 5 |
say "pointer to ..." |
| char _ _ _ _ _ _ _ _ _ _ _; |
step 6 |
say "char"
|
Then put it all together to read:
"next is a pointer to a function returning a pointer to a read-only pointer-to-char"
and we're done.
Examples from class
Now try the example in class:
void (*signal(int signum, void (*handler)(int)))(int);
If you like, do "man signal" to see what signal actually does. Note,
too, that the declaration could have been made worse by saying:
void (*signal(int, void (*)(int)))(int);
Or better by saying:
typedef void (*sig_handler)(int);
sig_handler signal(int, sig_handler);
|