From: anderson@Software.Mitel.COM (David Anderson)
Newsgroups: comp.lang.c
Subject: Re: Suspicious pointer conversion?
Summary: Clockwise Spiral Rule to help in parsing complicated declarations
Date: 8 Apr 92 18:29:04 GMT
Organization: Mitel. Kanata (Ontario). Canada.

In article <57006@cup.portal.com> ts@cup.portal.com (Tim W Smith) writes:
>>   To declare signal(), using prototypes, but without typedef, we must%
>>   write:
>>
>>      void (*signal(int, void (*)(int)))(int);
>>
>>   This is not particularly readable. [...]
>>
>>
>> So how does one parse such an expression?  If I understood the rules at this
>> level, I would be able to write these things much easier rathyer than by trial
>> and error.
>

I have been using a very easy rule to reading complicated declarations that I
use when teaching C for the College, called the "Clockwise Rule". I read
about it in an article sometime ago... anyway, it is so easy to use that
you don't even need to write anything down to parse it all in your head!

Here is how it works:

1) Starting with the unknown element, move in a spiral/clockwise direction and
   when you ecounter the following elements replace them with the corresponding
   english statements:

    [X] or []       = Array X size of... or Array undefined size of...
    (type1, type2)  = function passing type1 and type2 returning...
    *               = pointer(s) to...

2) Keep doing this in a spiral/clockwise direction until all tokens are covered
3) Always resolve anything in parenthesis first!

Some examples are required to fully understand this concept.

Example #1: Simple declaration

                     +-------+
                     | +-+   |
                     | ^ |   |
                char *str[10];
                 ^   ^   |   |
                 |   +---+   |
                 +-----------+

Question we ask ourselves: What is str?

                "str is an...

o We move in a spiral clockwise direction starting with 'str' and the first
  character we see is a '[' so, that means we have an array, so...

                "str is an array 10 of...

o Continue in a spiral clockwise direction, and the next thing we encounter is
  the '*' so, that means we have pointers, so...

                "str is an array 10 of pointers to...

o Continue in a spiral direction and we see the end of the line (the ';' ) so
  keep going and we get to the type 'char', so...

                "str is an array 10 of pointers to char"

o We have now "visited" every token; therefore we are done!

============================================================================

Example #2: Pointer to Function declaration

                     +--------------------+
                     | +---+              |
                     | |+-+|              |
                     | |^ ||              |
                char *(*fp)( int, float *);
                 ^   ^ ^  ||              |
                 |   | +--+|              |
                 |   +-----+              |
                 +------------------------+

Question we ask ourselves: What is fp?

  "fp is a...

o Moving in a spiral clockwise direction, the first thing we see is a ')';
  therefore, fp is inside parenthesis, so we continue the spiral inside the
  parenthesis and the next character seen is the '*', so...

  "fp is a pointer to...

o We are now out of the parenthesis and continuing in a spiral clockwise
  direction, we see the '('; therefore, we have a function, so...

  "fp is a pointer to a function passing an int and a pointer to float
   returning...

o Continuing in a spiral fashion, we then see the '*' character, so...

  "fp is a pointer to a function passing an int and a pointer to float
   returning a pointer to...

o Continuing in a spiral fashion we see the ';', but we haven't visited all
  token so we continue and we finally get to the type char, so...

  "fp is a pointer to a function passing an int and a pointer to float
   returning a pointer to a char"

============================================================================

Example #3: The original posters dilemma

                      +-----------------------------+
                      |                  +---+      |
                      |  +---+           |+-+|      |
                      |  ^   |           |^ ||      |
                void (*signal(int, void (*fp)(int)))(int);
                 ^    ^      |      ^    ^  ||      |
                 |    +------+      |    +--+|      |
                 |                  +--------+      |
                 +----------------------------------+

Question we ask ourselves: What is 'signal'?

Notice that signal is *inside* parenthesis, so we must resolve this first!

o Moving in a *clockwise* direction we see '(' so we have...

  "signal is a function passing an int and a...

o Hmmm, we can use this same rule on 'fp', so... What is fp? fp is also inside
  parenthesis so continuing we see an '*', so...

  "fp is a pointer to...

o Continue in a spiral clockwise direction and we get to '(', so...

  "fp is a pointer to a function passing int returning..."

o Now we continue out of the function parenthesis and we see void, so...

  "fp is a pointer to a function passing int returning nothing (void)"

o We have finished with fp so let's catch up with 'signal', we now have...

  "signal is a function passing an int and a pointer to a function passing an
   int returning nothing (void) returning...

o We are still inside parenthesis so the next character seen is a '*', so...

  "signal is a function passing an int and a pointer to a function passing an
   int returning nothing (void) returning a pointer to...

o We have now resolved the items within parenthesis, so continuing clockwise,
  we then see another '(', so...

  "signal is a function passing an int and a pointer to a function passing an
   int returning nothing (void) returning a pointer to a function passing an
   int returning...

o *Finally* we continue and the only thing left is the word 'void', so the
final complete definition for signal is:

  "signal is a function passing an int and a pointer to a function passing an
   int returning nothing (void) returning a pointer to a function passing an
   int returning nothing (void)"

=============================================================================

This explanation is long only because I broke it down into separate sections,
once you get used to it, anyone can "whip" off any declaration within seconds!

It also helps to use this rule when trying to determine if your declaration
makes sense.

One last point, for things like const and volatile and far etc..., always
associate them from left to right; for example:

        const char *chptr;

o Associate const with the token to the right, so in your mind imagine as if
  it was declared as follows:

        (const char) *chptr;

o Now, what is chptr??

  "chptr is a pointer to a constant char"

How about this one:

        char const *chptr;

o Associate the const with the token to the right and you then imagine:

       char (const *)chptr;

o Now, what is chptr??

  "chptr is a constant pointer to char"

This technique has really been useful in helping my students get through
complicated declarations, with practice you should be able to parse any
declaration thrown at you...

Practice this on the examples in K&R II on page 122.

Hope this helps,

Regards,

/David
-- 
-------------------------------------------------------
David AR Anderson   Email: anderson@Software.Mitel.COM
Software Tools and Integration
Mitel Corporation   Disclaimer: My opinions are my own!