top | item 13438613

(no title)

dnquark | 9 years ago

The trick to reading crazy C declarations is learning the "spiral rule": http://c-faq.com/decl/spiral.anderson.html (here are more examples, with nicer formatting: http://www.unixwiz.net/techtips/reading-cdecl.html)

discuss

order

paulnechifor|9 years ago

The "spiral rule" doesn't work all the time. See this previous HN comment by stephencanon: https://news.ycombinator.com/item?id=12775862 .

Also this comment by Linus Torvalds (copy pasted because I don't know how to link to Google+ comments):

> I don't think that works. It breaks trivially for consecutive [] or * cases, something that he carefully didn't have in his examples.

> So the examples were made up to make it look like it's a spiral, but type parsing is about precedence, not about spirals. It so happens that the higher-precedence operators ([] and ()) are on the right-hand side, which is why it "works" to start on the right.

> And it doesn't explain why

> typedef int (fn_t[][2])(void);

> is ok, but

> typedef int (fn_t[2][])(void);

> is not.

> "Spirals"? I don't think so.

dnquark|9 years ago

All these years I assumed that the 'spiral rule' and 'right-left rule' (linked from the stephencanon comment above, and also described in my second link) are two ways to describe the same algorithm, but, reading closely, they aren't! I guess 'spiral rule' is a stickier name, which is why it's something that people remember even though it's janky.

a_t48|9 years ago

    typedef int *(**fn_t[][2])(void);
is ok, but

    typedef int *(**fn_t[2][])(void);
is not.

(fixed formatting?)

colanderman|9 years ago

No, the trick is to remember that declaration follows use. Declare a symbol using (nearly) the same exact syntax you would use to extract a value of the base type from that symbol.

See also my comment last time this subject came up: https://news.ycombinator.com/item?id=12775966

Y_Y|9 years ago

And yet so many people learn

  int* p; // p is an int pointer
instead of

  int *p; // dereferencing p will give an int
I know this is the subject of holy wars, but once I'd seen the second one my eyes were opened and I had way less trouble. I think that declaration follows use is another of example of the amazing design powers of the patriarchs.

ramshorns|9 years ago

So, applied to the examples:

    As a variable:
    returnType (*variableName)(parameterTypes) = function_name;
variableName is a pointer to a function that accepts parameterTypes and returns returnType

    As a static const variable:
    static returnType (* const variableName)(parameterTypes) = function_name;
variableName is a const pointer to a function that accepts parameterTypes and returns static returnType (or maybe variableName is the static thing?)

    As an array:
    returnType (*arrayName[])(parameterTypes) = {function_name0, ...};
arrayName is an array of pointers to functions that accept parameterTypes and return returnType

    As an argument to a function:
    int my_function(returnType (*argumentName)(parameterTypes));
my_function is a function that accepts an argumentName, which is a pointer to a function accepting parameterTypes and returning returnType, and returns an int

    As a return value from a function:
    returnType (*my_function(int, ...))(parameterTypes);
my_function is a function that accepts int and other parameters and returns a pointer to a function that accepts parameterTypes and returns returnType

    As a typedef:
    typedef returnType (*typeName)(parameterTypes);
a typeName is now a pointer to a function that accepts parameterTypes and returns returnType