
No more confusions on tricky C declarations - ashishb4u
http://c-faq.com/decl/spiral.anderson.html
======
tptacek
I've always had a bit more luck with the "typedef each step of the
construction" rule-of-thumb. Also, I tend to hide anything as complex as
"pointer-to-array-of-pointers-to-functions" (even though you memorize this
idiom pretty quickly after an hour in the kernel) behind library ADT's, so
you're never indexing an array, but rather passing an index and a whatever_t*
to whatever_get(w, index).

~~~
scott_s
That's a good approach, but sometimes you can't do it in C++. (At least with
C++03.) Consider:

    
    
      template <class T>
      T* new_align_1d(size_t d1, unsigned int align)
      {
        void* ptr = _malloc_align(d1 * sizeof(T), align);
        return new (ptr) T[d1];
      }
    

So, I'm defining a function _new_align_ that takes a size and an alignment. I
allocate enough space for a 1-dimensional array of the given size on that
alignment, then use the placement operator to construct the actual objects in
that place. Then return the pointer. Pretty straightforward. So let's
generalize one dimension up:

    
    
      template <class T, size_t d2>
      ? new_align_2d(size_t d1, unsigned int align)
      {
        void* ptr = _malloc_align(d1 * d2 * sizeof(T), align);
        return new (ptr) T[d1][d2];
      }
    

You can probably see what I'm doing here. So what's the return type? And
what's the syntax for specifying it? Since it's a template, we can't define a
typedef to help us. (C++0x should allow that with parameterized typedefs.) The
answer surprised me - I really had to look at the grammar and mechanically
figure out what it was going to be.

(Yes, I have to pass d2 as a template parameter - it's part of the type of the
array and must be known at compile time.)

~~~
apu
The fact that I instantly recognized this problem case (having had it many
times myself) makes me sad.

I'm so incredibly happy that 95% of the code I write now is in Python.

------
loup-vaillant
An ML like notation would be even more cool:

    
    
      char *str[10];
      str : [10] (*char)
    
      char *(*fp)( int, float *);
      fp : *((int, *float) -> *char)
    
      void (*signal(int, void (*fp)(int)))(int);
      signal : (int, *(int -> void)) -> *(int -> void)
    

(Oh. That last declaration did make some sense, after all…)

Really, how did they manage to chose such an inconsistent, unreadable syntax
for their declarations? Is there any rational explanation?

~~~
endgame
AIUI, the explanation is that they wanted this idea of "declaration follows
use". For simple things, this works kind of nicely:

    
    
        char *foo;
    

Says that *foo will have type `char`. This sounds good in theory, but famously
breaks down when things get more complicated (arrays of function pointers,
multiple declarations at once, &c.).

~~~
mturmon
Yes, see van der Linden's Deep C Secrets:

[http://books.google.com/books?id=4vm2xK3yn34C&printsec=f...](http://books.google.com/books?id=4vm2xK3yn34C&printsec=frontcover&dq=deep+c+secrets&source=bl&ots=Hna8W92Id0&sig=G7re-
qV-7tRMddDVnG_e-6f0GZ8&hl=en&ei=fcYmTMbeIcf7nAe_2ri8Bg&sa=X&oi=book_result&ct=result&resnum=5&ved=0CCgQ6AEwBA#v=onepage&q&f=false)

------
rntz
Fails on nested arrays.

    
    
      char *foo[10][20];
    

The method described would indicate that this is a array 10 of pointers to
array 20s of chars.

This is incorrect. It is an array 10 of array 20s of pointers to chars.

~~~
tordek
How so?

    
    
             +-----------+
             | +-+       |
             | ^ |       |
        char *foo[10][20];
         ^   ^   |       |
         |   +---+       |
         +---------------+
    

* foo is

* an array of ten arrays of 20

* pointers to

* char

~~~
rntz
Following the procedure as written:

    
    
             +-------+ 
             | +-+   |
             | ^ |   |
        char *foo[10][20];
          ^  ^   |   |
          |  +---+   |
          -----------+
    

It mentions, in rule 1, handling tokens of the form [] or [X], but not
multiple occurrences of these.

This is indeed rather nitpickish of me, but on the other hand, one thing that
you at least need to know in that case is in what order you read the numbers -
left-to-right, or right-to-left (is foo[10][20] a 10-array of 20-arrays or a
20-array of 10-arrays?). Not mentioning this leaves one (at least, it left me)
with the impression that multiple occurrences were already handled by this
rule as written, which is, as demonstrated, false.

------
jeffmax
<http://cdecl.org/>

~~~
exit
this is fantastic, but apparently "void ( _signal(int, void (_
fp)(int)))(int);" is a syntax error?

~~~
loup-vaillant
It is not, but their implementation is incomplete. They parse it correctly
when you remove "fp". Apparently, they don't handle declarations which have
another identifiers besides the one that is declared.

    
    
      int f(int  ) // OK
      int f(int i) // fail

------
Amnon
I don't see where the spiral comes in. The following rule is simpler:

(1) Begin at the variable name, read from left to right, then go back to the
name and read from right to left.

(2) Give precedence to expressions in parentheses.

For example: char _(_ fp)( int, float * )

The innermost expression is (* fp). Nothing to the right of the fp. so read to
the left: "* ", it's a pointer. Next, we go right and see an arguments list,
so it's a pointer to a function taking these arguments. Go back to where we
started and read right to left: Pointer to a function taking (int,float *)
that returns a pointer to char.

~~~
ashishb4u
left-to-right and right-to-left is spiral infact :)

------
rue

        char* str[10]; /* Better */

~~~
gmartres
I disagree, the following:

    
    
      int* foo, bar;

could be interpreted as declaring two pointers, whereas:

    
    
      int *foo, bar;

makes it clear that only the first variable is a pointer.

~~~
godDLL
I don't think you should be declaring mixed types with one statement. Declare
a pointer, then declare the `int` separately -- and the mystery is gone.

~~~
masklinn
I believe the point is that, with that syntax, you can't declare multiple
same-type pointers on the same line

------
MtL
Meh! This is just an overly complication of the right-left rule, which makes
you think about "complex" 2D geometry instead of a couple of simple spatial
pointers in the declaration you are trying to parse..

The easier, more useful version:
<http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html>

------
joe_the_user
It is amazing to me the number of people who would take the time to make ASCII
graphics in their replies.

I'm blessed that even munging 15+ Linux c/c++ libraries lately I haven't run
into anything requiring this - though my Intro to C class, at Merit College
twenty five years ago did teach to this rule.

Hats off to you anyway...

------
AndrejM
No problems here, that is, with D's right-to-left declaration syntax. ;)

~~~
mkramlich
yes this is the kind of thread I expect to see @WalterBright chime in on :)

------
ashishb4u
Sure helps to read code faster!

