will@kfw.COM (Will Crowder) (07/16/90)
I've received several mail messages since offering to explain the right-left rule via e-mail, so I've decided to post. For those of you who already know this or don't care, quit now (this is a little long). Here's a control-L: Thanks for sticking around... :) :) :) The "right-left" rule is a completely regular rule for deciphering C declarations. It can also be useful in creating them. First, symbols. Read * as "pointer to" [] as "array of" () as "function returning" as you encounter them in the declaration. STEP 1 ------ Find the identifier. This is your starting point. Then say to yourself, "identifier is". You've started your declaration. STEP 2 ------ Look at the symbols on the right of the identifier. If, say, you find "()" there, then you know that this is the declaration for a function. So you would then have "identifier is function returning". Or if you found a "[]" there, you would say "identifier is array of". Continue right until you run out of symbols *OR* hit a *left* parenthesis. (If you hit a right parenthesis, that's the beginning of a () symbol, even if there is stuff in between the parentheses. More on that below.) STEP 3 ------ Look at the symbols to the left of the identifier. If it is not one of our symbols above (say, something like "int"), just say it. Otherwise, translate it into English using that table above. Keep going left until you run out of symbols *OR* hit a *right* parenthesis. Now repeat steps 2 and 3 until you've formed your declaration. Here are some examples: int *p[]; 1) Find identifier. int *p[]; ^ "p is" 2) Move right until out of symbols or left parenthesis hit. int *p[]; ^^ "p is array of" 3) Can't move right anymore (out of symbols), so move left and find: int *p[]; ^ "p is array of pointer to" 4) Keep going left and find: int *p[]; ^^^ "p is array of pointer to int". Another example: int *(*func())(); 1) Find the identifier. int *(*func())(); ^^^^ "func is" 2) Move right. int *(*func())(); ^^ "func is function returning" 3) Can't move right anymore because of the left parenthesis, so move left. int *(*func())(); ^ "func is function returning pointer to" 4) Can't move left anymore because of the right parenthesis, so keep going right. int *(*func())(); ^^ "func is function returning pointer to function returning" 5) Can't move right anymore because we're out of symbols, so go left. int *(*func())(); ^ "func is function returning pointer to function returning pointer to" 6) And finally, keep going left, because there's nothing left on the right. int *(*func())(); ^^^ "func is function returning pointer to function returning pointer to int". Whew! (Brief pause whilst the author wipes the sweat off his brow.) As you can see, this rule can be quite useful. You can also use it to sanity check yourself while you are creating declarations, and to give you a hint about where the put the next symbol and whether parentheses are required. Some declarations look much more complicated than they are due to array sizes and argument lists in prototype form. If you see "[3]", that's read as "array (size 3) of...". If you see "(char *,int)" that's read as "function expecting (char *,int) and returning...". Here's a fun one: int (*(*fun_one)(char *,double))[9][20]; I won't go through each of the steps to decipher this one. Here's a control-L. Press <return> (or whatever) to see what this one is. Ok. It's: "fun_one is pointer to function expecting (char *,double) and returning pointer to array (size 9) of array (size 20) of int." As you can see, it's not as complicated if you get rid of the array sizes and argument lists: int (*(*fun_one)())[][]; You can decipher it that way, and then put in the array sizes and argument lists later. Some final words: It is quite possible to make illegal declarations using this rule, so some knowledge of what's legal in C is necessary. For instance, if the above had been: int *((*fun_one)())[][]; it would have been "fun_one is pointer to function returning array of array of ^^^^^^^^^^^^^^^^^^^^^^^^ pointer to int". Since a function cannot return an array, but only a pointer to an array, that declaration is illegal. Anyway, there it is folks. Clarifications via e-mail (and boy I hope I didn't slip this one up!). Will will@kfw.com uunet!charyb!will
will@kfw.COM (Will Crowder) (07/17/90)
In the above-referenced article, I screwed up, and demonstrated that at age 26, I still can't tell the difference between left and right. Thanks to uunet!andrew.cmu.edu!ghoti+ (Adam Stoller) for pointing out the error. :) :) Here is the corrected article: I've received several mail messages since offering to explain the right-left rule via e-mail, so I've decided to post. For those of you who already know this or don't care, quit now (this is a little long). Here's a control-L: Thanks for sticking around... :) :) :) The "right-left" rule is a completely regular rule for deciphering C declarations. It can also be useful in creating them. First, symbols. Read * as "pointer to" [] as "array of" () as "function returning" as you encounter them in the declaration. STEP 1 ------ Find the identifier. This is your starting point. Then say to yourself, "identifier is". You've started your declaration. STEP 2 ------ Look at the symbols on the right of the identifier. If, say, you find "()" there, then you know that this is the declaration for a function. So you would then have "identifier is function returning". Or if you found a "[]" there, you would say "identifier is array of". Continue right until you run out of symbols *OR* hit a *right* parenthesis ")". (If you hit a left parenthesis, that's the beginning of a () symbol, even if there is stuff in between the parentheses. More on that below.) STEP 3 ------ Look at the symbols to the left of the identifier. If it is not one of our symbols above (say, something like "int"), just say it. Otherwise, translate it into English using that table above. Keep going left until you run out of symbols *OR* hit a *left* parenthesis "(". Now repeat steps 2 and 3 until you've formed your declaration. Here are some examples: int *p[]; 1) Find identifier. int *p[]; ^ "p is" 2) Move right until out of symbols or left parenthesis hit. int *p[]; ^^ "p is array of" 3) Can't move right anymore (out of symbols), so move left and find: int *p[]; ^ "p is array of pointer to" 4) Keep going left and find: int *p[]; ^^^ "p is array of pointer to int". Another example: int *(*func())(); 1) Find the identifier. int *(*func())(); ^^^^ "func is" 2) Move right. int *(*func())(); ^^ "func is function returning" 3) Can't move right anymore because of the right parenthesis, so move left. int *(*func())(); ^ "func is function returning pointer to" 4) Can't move left anymore because of the left parenthesis, so keep going right. int *(*func())(); ^^ "func is function returning pointer to function returning" 5) Can't move right anymore because we're out of symbols, so go left. int *(*func())(); ^ "func is function returning pointer to function returning pointer to" 6) And finally, keep going left, because there's nothing left on the right. int *(*func())(); ^^^ "func is function returning pointer to function returning pointer to int". Whew! (Brief pause whilst the author wipes the sweat off his brow.) As you can see, this rule can be quite useful. You can also use it to sanity check yourself while you are creating declarations, and to give you a hint about where the put the next symbol and whether parentheses are required. Some declarations look much more complicated than they are due to array sizes and argument lists in prototype form. If you see "[3]", that's read as "array (size 3) of...". If you see "(char *,int)" that's read as "function expecting (char *,int) and returning...". Here's a fun one: int (*(*fun_one)(char *,double))[9][20]; I won't go through each of the steps to decipher this one. Here's a control-L. Press <return> (or whatever) to see what this one is. Ok. It's: "fun_one is pointer to function expecting (char *,double) and returning pointer to array (size 9) of array (size 20) of int." As you can see, it's not as complicated if you get rid of the array sizes and argument lists: int (*(*fun_one)())[][]; You can decipher it that way, and then put in the array sizes and argument lists later. Some final words: It is quite possible to make illegal declarations using this rule, so some knowledge of what's legal in C is necessary. For instance, if the above had been: int *((*fun_one)())[][]; it would have been "fun_one is pointer to function returning array of array of ^^^^^^^^^^^^^^^^^^^^^^^^ pointer to int". Since a function cannot return an array, but only a pointer to an array, that declaration is illegal. Anyway, there it is folks. Clarifications via e-mail (and boy I hope I didn't slip this one up!). Will will@kfw.com uunet!charyb!will
karl@haddock.ima.isc.com (Karl Heuer) (07/17/90)
In article <1990Jul16.195111.5976@kfw.COM> will@kfw.com (Will Crowder) writes: >[150 lines about the right-left rule] Summary: in declarations as well as in expressions, postfix unary has higher precedence than prefix unary; parens may be used to override this. See also: cdecl(1). Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint
dankg@tornado.Berkeley.EDU (Dan KoGai) (07/17/90)
Thanks for your great article. And that reminded me of the one of the IOCCC winner's code: That takes C expression and parses it to English (i.e. "int (*foo())[]" -> foo is array of ptr to function that returns int). I had that code but it was lost due to damn cracker who messed my accout (sigh). But it was greate, useful, and well, obfuscunated. I don't know the server that retrieves IOCCC winner of the past but it's worth taking a look. ---------------- ____ __ __ + Dan The "Precedence sucks sometimes" Man ||__||__| + E-mail: dankg@ocf.berkeley.edu ____| ______ + Voice: +1 415-549-6111 | |__|__| + USnail: 1730 Laloma Berkeley, CA 94709 U.S.A |___ |__|__| + Oxymora: Usable MS-DOS, Multitasking Mac, Secure Unix, |____|____ + rec.humor posters without sense of humor, \_| | + Christian Science, and English grammer
korsberg@abaa.uucp (Ed Korsberg) (07/17/90)
In article <1990Jul16.195111.5976@kfw.COM> will@kfw.com (Will Crowder) writes: >[150 lines about the right-left rule] I have a table driven FSM with a state variable (state_variable) which points to one of several state tables (see trivial example below). Given the current state (state_variable) and an event, I want to call the appropriate action (func_x in tables). void (*state1_table[]) () = { func_1 /* EVENT 1 */ ,func_2 /* EVENT 2 */ ,func_3 /* EVENT 3 */ ,func_4 /* EVENT 4 */ }; void (*state2_table[]) () = { func_1 /* EVENT 1 */ ,func_2 /* EVENT 2 */ ,func_7 /* EVENT 3 */ ,func_6 /* EVENT 4 */ }; void (*state3_table[]) () = { func_1 /* EVENT 1 */ ,func_8 /* EVENT 2 */ ,func_0 /* EVENT 3 */ ,func_4 /* EVENT 4 */ }; fsm(event) unsigned int event; /* occurance of EVENT n */ { void (*action)(); action = state_variable[event]; /* From STATE and EVENT, get action */ (*action)(); /* perform action */ } **************************************************************************** Problem is how do you declare a pointer to an array of pointers of functions returning void? I need something like void (*state_variable)[] but cannot seem to get the syntax correct. Any help on this would be appreciated. --------------------------------------------------------------------------- Ed Korsberg korsberg@aa.ab.com
will@kfw.COM (Will Crowder) (07/18/90)
In article <1990Jul17.125413.7968@agate.berkeley.edu> dankg@tornado.Berkeley.EDU (Dan KoGai) writes: > Thanks for your great article. You're welcome. > And that reminded me of the one >of the IOCCC winner's code: That takes C expression and parses it to >English (i.e. "int (*foo())[]" -> foo is array of ptr to function that >returns int). Actually, foo is a function that returns a pointer to an array of ints. I have a Turbo C hack I call c2eng which will take the above and emit English for it. I'd be happy to e-mail it (uuencoded, compressed) to whomever wants it. Just send me mail, folks! It's not obfuscated... >---------------- >____ __ __ + Dan The "Precedence sucks sometimes" Man Will will@kfw.com uunet!charyb!will
will@kfw.COM (Will Crowder) (07/18/90)
In article <5593@abaa.UUCP> korsberg@abaa.UUCP (Ed Korsberg) writes: [description of a table-driven FSM which requires....] >[...] a pointer to an array of pointers of functions >returning void. void (*(*foo)[])(); Ugly, ain't it? >Ed Korsberg korsberg@aa.ab.com Will will@kfw.com uunet!charyb!will
jk@hpfelg.HP.COM (John Kessenich) (07/19/90)
>Summary: in declarations as well as in expressions, postfix unary has higher >precedence than prefix unary; parens may be used to override this. K&R treats punctuation, i.e. ()[].-> as operators having the highest precedence. Thus, this all reduces to "Declarations may be read correctly by starting with the name and then reading in precedence order." This makes more sense (and seems simpler) to me than rules like "inside out", "alternating left and right", etc. which seem like artificial structures added on top of the language. -------------- John Kessenich
johnb@srchtec.UUCP (John Baldwin) (07/19/90)
Does anyone have a PD or shareware version (preferably source code) for an English <--> C Complex Declarator translator? I know there's a version in K&R 2nd ed.; what I'm looking for is a robust version that can handle type qualifiers (like const), function argument types, and the like, and can recover from bad input (at least being able to recognize an error and say: "I can't translate that!"). -- John T. Baldwin | The opinions expressed herein are available research analyst | for the small price of $5. search technology, inc. | Operators are online 24 hours a day; call johnb@srchtec.uucp | 1-800-366-2277 (1-800-FOO-BARR).
jk@hpfelg.HP.COM (John Kessenich) (07/20/90)
>Problem is how do you declare a pointer to an array of pointers of functions >returning void? Lets see, void (*state_variable)(); read in precedence order would be a pointer to function returning void. To get a pointer to an array of these you need a pointer to one of these, void (**state_variable)(); Reading in precedence order gives a pointer to a pointer to a function that returns void, which is equivalent to saying a pointer to an array of functions returning void. To get an array of pointers to functions returning void, just use void (*state_variable[SIZE])(); -------------- John Kessenich
chris@mimsy.umd.edu (Chris Torek) (07/20/90)
In article <5593@abaa.UUCP> korsberg@abaa.uucp (Ed Korsberg) writes: >I have a table driven FSM with a state variable (state_variable) which >points to one of several state tables .... >void (*state1_table[]) () = >{ > func_1 /* EVENT 1 */ > ,func_2 /* EVENT 2 */ > ,func_3 /* EVENT 3 */ > ,func_4 /* EVENT 4 */ >}; Two comments: these are actually events 0..3, not 1..4; C syntax allows trailing commas so that you can write void (*tab[])() = { f0, f1, f2, f3, }; [more tables omitted] >fsm(event) >unsigned int event; /* occurance of EVENT n */ >{ > void (*action)(); > > action = state_variable[event]; /* From STATE and EVENT, get action */ > (*action)(); /* perform action */ >} > >**************************************************************************** >Problem is how do you declare a pointer to an array of pointers of functions >returning void? Here is a danger sign: if you ever want a `pointer to an array', think hard about it. In particular, think hardest about the N in the array. Every array is an `array N of T', where N is some constant. If you do not know the constant, you probably do not want a pointer to the (entire) array. Logically, a pointer to the whole array will do you no good, because you do not know how many array elements that pointer points to. You might as well instead point to one array element at a time---if you can. This follows from The Rule: In any value context, an object of type `array N of T' becomes a value of type `pointer to T' whose value is the address of the first (0th) element of that array. Thus, instead of a `pointer to array N of T', you most often want a `pointer to T'. (Note, however, that the Rule stops applying once you have a pointer. Thus, if you have an `array N of T' where T is, say, `array 3 of int', you wind up with a `pointer to T', or a `pointer to array 3 of int'. This is not an object of type `array N of ...', it is a value of type `pointer to ...'; it does not get converted again, and you will need to know the (new) N in the sub-array that is part of `T'.) In other words, rather than `pointer to array N of T=pointer-to-function- of-void-returning-void', you probably really want `pointer to T', because each of your table objects is an `array N of T'. You could---given N, or deciding that `N=?'---get a pointer to each one, but you do not *have* to; it is more convenient to point to the first element of each table. Thus, you want: $ T='pointer to function returning void' $ echo "declare state_variable as pointer to $T" | cdecl void (**state_table)() $ If you really prefer it, you can fix an N (either 4 or ? will suffice for the example given) and write either void (*(*state_table)[4])() /* N=4 */ or void (*(*state_table)[])() /* N=? */ or either of those using typedef to make them `simpler'. You will then have to write, e.g., state_table = &state1_table; instead of the simpler state_table = state1_table; for the first declaration. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris