[comp.lang.c] declarations using pointers to functions

anon@rouge.usl.edu (Anonymous NNTP Posting) (03/21/91)

Hi,

Can someone tell me the "spiralling" or "circular" convention 

used in the complex variable declarations involving array of 
    
pointers to functions. 

I heard that there is a standard convention to be followed...

Thanx,

Rajiv

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
EMAIL: rxd0219@usl.edu
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

gordon@osiris.cso.uiuc.edu (John Gordon) (03/21/91)

	Here is a document that sounds like what you want:

----------------------------------
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.

torek@elf.ee.lbl.gov (Chris Torek) (03/21/91)

In article <1991Mar20.201937.6088@ux1.cso.uiuc.edu> gordon@osiris.cso.uiuc.edu
(John Gordon) writes:
>     [] 	as "array of"

Actually, since there are only two cases where the brackets may be empty,
I would suggest instead reading

	[N]	as "array N of"

and
	[]	as "array ? of"

and NEVER forget the sizes until The Rule allows you to do so.

The size of any array is an important part of the type of that array;
it must not be disregarded at whim.

(The Rule allows you to discard the size whenever an <object, array N of T>
is used in a value context.  The two cases where the brackets may be
empty occur when declaring formal parameters to functions, and when
declaring `extern' objects.  Even then, only the `innermost' set of
brackets may be empty, and only if they immediately follow the
identifier.)
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

willcr@bud.sos.ivy.isc.com (Will Crowder) (03/22/91)

In article <11208@dog.ee.lbl.gov>, torek@elf.ee.lbl.gov (Chris Torek) writes:

|> In article <1991Mar20.201937.6088@ux1.cso.uiuc.edu>
|> gordon@osiris.cso.uiuc.edu
|> (John Gordon) writes:

[Actually, I wrote the document in question and posted it several months
ago in response to slew of "how do I declare..." questions.]

|> >     [] 	as "array of"
|> 
|> Actually, since there are only two cases where the brackets may be empty,
|> I would suggest instead reading
|> 
|> 	[N]	as "array N of"
|> 
|> and
|> 	[]	as "array ? of"
|> 
|> and NEVER forget the sizes until The Rule allows you to do so.

Agreed.  However, at the time, I was trying to keep things simple, but
you've brought up a valuable point.  I think I'll add The Rule (see
original article; don't want to quote too much!) to a little C declaration
syntax checker I've been hacking, er, uh, working on...  :) :)

Will

--------------------------------------------------------------------------------
Will Crowder, MTS            | "I belong to no organized politcal party.  
(willcr@ivy.isc.com)         |  I'm a democrat."  
INTERACTIVE Systems Corp.    |		-- Will Rogers

sparks@power.viewlogic.com (Alan Sparks) (03/26/91)

In article <27514@rouge.usl.edu> anon@rouge.usl.edu (Anonymous NNTP Posting) writes:
>Hi,
>
>Can someone tell me the "spiralling" or "circular" convention 
>
>used in the complex variable declarations involving array of 
>    
>pointers to functions. 
>
>I heard that there is a standard convention to be followed...
>
>Thanx,
>
>Rajiv
>
>%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>EMAIL: rxd0219@usl.edu
>%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


When I used to teach C, I used a book entitled "Advanced C Tips and
Techniques" by Gail and Paul Anderson in the second semester of the 
class.  They documented a rule called the "right-left rule," which
I'll try to condense here.

[loosely copied, used without permission]

Define the attribute/keyword relationship:

	Attribute	English keyword
	  ()		function returns
	  [n]		array of n
	  *             pointer to

To decipher a declaration, substitute the English keyphrase where an
attribute appears.

Now the rule:

	1. Start with the identifier.
	2. Look to the right for an attribute.	
	3. If none found, look to the left.
	4. If found, substitute the attribute.
	5. Continue right-left substitution as you work outward.
	6. Stop when you reach the data type in the declaration.

Remember that multidimensional arrays use the [] attribute more than
once; in this case, continue to the right until you run out of []
before you look left.

Let's try a couple...

	struct something *ps[3][4];
		ps is an array of 3 arrays of 4 pointers to struct something.

	int (*pf[5])()

		pf is an array of 5 pointers to function returning int.

The rule can be used backwards also.  Basically, substitute the attribute
where the English keyphrase occurs in your statement of what you need
(but see the caveat below):

	buf is array of ten pointers to function returning pointer to int.

		int *(*buf[10])();

Why the intervening parentheses?  Certain combinations of attributes
are illegal (not defined by C):

		[]()	functions cannot return arrays; only pointers to.
		[]()	arrays of functions; you CAN have arrays of
			function *pointers*, however.
		()()	functions can't return functions; you CAN return
			a *pointer* to a function, however.

Parentheses must be used to keep the lower precedence operator (namely
*) grouped with the correct higher precedence operator (like [] or ()).
Therefore, 
		int *foo[]()
isn't legal (foo is array of function returning int *) {precedence of
[] and () is higher than that of *} but

		int (*foo[])()
IS legal (foo is array of pointer to function returning int).

I know it gets kinda confusing (C can get confusing anyway).  Hope this
might be a helpful start.  (Want a real-life example?  Check out the man
page for signal(3).)

-Alan
-- 

Alan Sparks      voice: (508) 480-0881  Internet: sparks@viewlogic.com
VIEWlogic Systems Inc., 293 Boston Post Rd. W., Marlboro MA 01752
Disclaimer: VIEWlogic didn't say this; I might have.  Who's asking?

msb@sq.sq.com (Mark Brader) (03/26/91)

Chris Torek (torek@elf.ee.lbl.gov) writes:

> Actually, since there are only two cases where the brackets may be empty,
> I would suggest instead reading
> 	[N]	as "array N of"
> and
> 	[]	as "array ? of"
> and NEVER forget the sizes until The Rule allows you to do so.

It's evident from his postings that Chris is happy with this locution.
For those of us who speak normal English, however, I suggest reading

	[N]	as "array of N ...s"
and
	[]	as "array of unknown number of ...s".

where ... is replaced by the appropriate type name.  I also suggest,
incidentally, reading

	*	as "pointer to ...s".

Note the plural, which is nonstandard C terminology; this is intended to
emphasize that it may point to the beginning of an array of the type.

-- 
Mark Brader, Toronto	    "If you feel [that Doug Gwyn] has a bad attitude,
utzoo!sq!msb, msb@sq.com     then use lint (or Chris Torek...)" -- Joe English

This article is in the public domain.

morse@hatteras.cs.unc.edu (Bryan Morse) (03/26/91)

In article <1991Mar25.171853.4229@viewlogic.com> sparks@power.viewlogic.com (Alan Sparks) writes:
>In article <27514@rouge.usl.edu> anon@rouge.usl.edu (Anonymous NNTP Posting) writes:
>>Hi,
>>
>>Can someone tell me the "spiralling" or "circular" convention 
>>
>>used in the complex variable declarations involving array of 
>>    
>>pointers to functions. 
>>
>>I heard that there is a standard convention to be followed...
>

[replying explanation of the right-left rule omitted]


Actually, I've always found that the easiest was to understand and to 
write C declarations is to write them out as you would use them.
This is one of the simplicities (!?!?) of C declarations.

Remember that C declarations are of the form:

	<type> <expression that evaluates to that type>

So, just write the expression.
 
>
>	buf is array of ten pointers to function returning pointer to int.
>
>		int *(*buf[10])();
>

Okay, start with the name:

		buf

Since it is an array, index into it:
		
		buf[]

Array items are pointers.  Pointers are dereferenced, right?

		*buf[]

These pointers are to functions?  Functions are called.
(Here it is somewhat tricky to remember the precedence, but just remember
that right () or [] has higher precedence than left *.)

		(*buf[])()

These functions return pointers.  Okay, dereference them.

		*(*buf[])()

These pointers are integers.

		int *(*buf[])()

Fill in the array bounds and add a semicolon and you're done.

It's really that simple.  Think about how you use it rather than simply
applying an English-to-C algorithm.

(Disclaimer: Anyone who really writes these things and doesn't use 
typedefs is crazy.)

-- 
Bryan Morse                University of North Carolina at Chapel Hill
morse@cs.unc.edu           Department of Computer Science