Live data from Hacker News

The Clockwise/Spiral Rule of C declarations

c-faq.com

1–10 of 72 posts

Re: The Clockwise/Spiral Rule of C declarations

#3
The rule is misleading in cases like:

    int* arr[][10];
Spiral rule would state "arr is an array of pointers to arrays of 10 ints", where actually it would be "arr is an array of array of 10 pointers to int".

Instead, when you write declarations, do it from right-to-left, e.g.:

   char const* argv[];
"argv is an array of pointers to constant characters"

It doesn't help with reading, unfortunately.

Re: The Clockwise/Spiral Rule of C declarations

#4
This has been posted before[1], and the "spiral rule" is a load of hooey.

The correct rule is "follow the C grammar". An easier to remember and also correct rule is "start at the identifier being declared; work outwards from that point, reading right until you hit a closing parenthesis, then left until you hit the corresponding open parenthesis, then resume reading right..." (this is sometimes called the "right-left rule"[2]).

The "spiral rule" dances around the truth without actually being precise enough to be useful.

[1] https://news.ycombinator.com/item?id=5079787 [2] http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html

Re: The Clockwise/Spiral Rule of C declarations

#6
post #3

The rule is misleading in cases like: int* arr[][10]; Spiral rule would state "arr is an array of pointers to arrays of 10 ints", where actually it would be "arr is an array of array of 10 pointers to int". Instead, when you write declarations, do it from right-to-left, e.g.: char const* argv[]; "argv is an array of pointers to constant characters" It doesn't help with reading, unfortunately.

I think the advice that helped me the most was "Declaration follows usage".

  int* arr[][10];
If you index twice into arr and then dereference, you'll get an int. So arr must be an array of array of pointer to int.

Re: The Clockwise/Spiral Rule of C declarations

#7
post #3

The rule is misleading in cases like: int* arr[][10]; Spiral rule would state "arr is an array of pointers to arrays of 10 ints", where actually it would be "arr is an array of array of 10 pointers to int". Instead, when you write declarations, do it from right-to-left, e.g.: char const* argv[]; "argv is an array of pointers to constant characters" It doesn't help with reading, unfortunately.

For this reason I strongly prefer writing

    char const
rather than

    const char
Is there a reason to prefer the second version? It's a lot more popular in my experience.

Re: The Clockwise/Spiral Rule of C declarations

#8
post #6
post #3

The rule is misleading in cases like: int* arr[][10]; Spiral rule would state "arr is an array of pointers to arrays of 10 ints", where actually it would be "arr is an array of array of 10 pointers to int". Instead, when you write declarations, do it from right-to-left, e.g.: char const* argv[]; "argv is an array of pointers to constant characters" It doesn't help with reading, unfortunately.

I think the advice that helped me the most was "Declaration follows usage". int* arr[][10]; If you index twice into arr and then dereference, you'll get an int. So arr must be an array of array of pointer to int.

Declaration follows usage is much more easier to follow than the artificial spiral rules, IMHO with a few typedefs the declaration follows usage can make things pretty simple.

Re: The Clockwise/Spiral Rule of C declarations

#9
Way simpler: from inside out, read any subpart of the type as an expression. (Arrays have precedence over pointers, as usual.) The type that remains is that expression's type. So e.g. given the type:

    const char *foo[][50]
the following expressions have the following types:

     foo       -> const char *[][50]
     foo[0]    -> const char *  [50]
     foo[0][0] -> const char *
    *foo[0][0] -> const char
Another example:

    int (*const bar)[restrict]
    
      bar     -> int (*const)[restrict]
     *bar     -> int         [restrict]
    (*bar)[0] -> int
One more:

    int (*(*f)(int))(void)
    
        f        -> int (*(*)(int))(void)
      (*f)       -> int (*   (int))(void)
      (*f)(0)    -> int (*        )(void)
     *(*f)(0)    -> int            (void)
    (*(*f)(0))() -> int
(In this case, the last expression is more readily written as f(0)(), since pointers to functions and functions are called using the same syntax.)
Post reply on HN