CIS 399

Right - Left Rule for C/C++


The following describes a way to read and write complex declarations in C and C++.

We start with a table that maps the symbolism used in declarations to their meaning and English interpretation along with their syntactic placement with respect to an identifier:
SymbolMeaningPlacement
()function returningright
[n]array of nright
*pointer toleft

Using this table, we can read a declaration as follows:

  1. Start with the identifier
  2. Look to the right for an attribute (the symbols listed above), and substitute the English phrase from the table
  3. If no attribute is found to the right, look to the left for an attribute and substitute the phrase from the table
  4. Repeat until we reach the data type

Of course, parentheses must be obeyed as we work our way out. That is, you cannot go to the right beyond a closing parenthesis until you have dealt with everything within the parentheses.

This rule is based on the precedence and associativity of the operators represented in the table as they are defined in the language.

To keep things simple, we are only showing functions that take no arguments, but the parentheses of the function could enclose a comma separated list of types specifying the parameter types.

Here is an example:

unsigned long totals[10];
Starting with the identifier totals, we look to the right and substitute "array of 10". That uses up everything and we have left the data type of unsigned long. So we read the declaration as
"totals is an array of 10 unsigned long".

Another example (where BigNum is a class):

BigNum *matrix[3][4];
Starting with the identifier matrix, we look to the right and substitute "array of 3". We look to the right again and substitute "array of 4". That uses up everything to the right, so we look to the left and substitute "pointer to". Now we are down to the data type "BigNum" and the result is
"matrix is an array of 3 arrays of 4 pointers to BigNum".

Here is an example with a pointer to a function:

int *(*pf[3])();
Starting with the identifier pf, we look to the right and substitute "array of 3". We look to the right again and see the parenthesis forces us to the left where we substitute "pointer to". Now we look to the right again and find "function returns". There is still some on the left, giving us another "pointer to", and then we come to the data type "int". Putting this together we have
"pf is an array of 3 pointers to functions returning pointers to int".

The method described above for reading declarations can also be used to write declarations, just by applying the substitutions in reverse. For example, if we want to write a declaration for fp as a pointer to a function returning an int, we start with the identifier fp and substitute the English phrases, putting them on the right or left as the syntax dictates.
fp is a pointer to  *fp
fp is a pointer to a function returning  (* fp)()
fp is a pointer to a function returning int  int (* fp)()

Note that not all combinations of these declaration symbols are legal syntax. For example, void foo()() is not legal. Intuitively, a function cannot return a function. Likewise, int foo()[] is not legal since a function cannot return an array.