Live data from Hacker News

The Clockwise/Spiral Rule of C declarations

c-faq.com

31–40 of 72 posts

Re: The Clockwise/Spiral Rule of C declarations

#31
post #15

Just don't make complex declarations in C, it's almost never useful and won't help anyone out. It'll confuse people and make your code write-only. Just put in a couple extra lines of code somewhere if you have to. It won't be the end of the world.

It'll certainly confuse people, but only those who aren't qualified to be doing anything with the code anyway. "complex" is subjective. It reminds me of stupid "rules" like "don't use the ternary operator", "every function must be less than 20 lines" (I am not exaggerating --- this was on a Java project, however); and you could easily extend that to "every statement must have a maximum of one operator", "you must not…

"if you write code that even an idiot can understand, only idiots will want to work on it."

To me, that makes about as much sense as when Ricky Bobby in Talladega Nights says "If you ain't first, you're last."

Re: The Clockwise/Spiral Rule of C declarations

#32
post #20

Earlier quoted context omitted.

It can also be simpler if you use idiomatic modern C++ with std::array and std::function .

Looking at "idiomatic modern C++", I am often at a loss for words at what lengths they've gone to in order to reinvent things while greatly obfuscating them in the process. Is there a std::pointer_to too? I don't know, but something like this std::array , 10> str; certainly does not look any more readable to me than byte *str[10]; . (Disclaimer: I mainly work with C, but find some C++ features genuinely useful, altho…

std::array does not exist because it is easier to read. It exists because C arrays behave strangely. Two examples: decay to pointer and no value semantics.

Re: The Clockwise/Spiral Rule of C declarations

#33
post #20

Earlier quoted context omitted.

It can also be simpler if you use idiomatic modern C++ with std::array and std::function .

Looking at "idiomatic modern C++", I am often at a loss for words at what lengths they've gone to in order to reinvent things while greatly obfuscating them in the process. Is there a std::pointer_to too? I don't know, but something like this std::array , 10> str; certainly does not look any more readable to me than byte *str[10]; . (Disclaimer: I mainly work with C, but find some C++ features genuinely useful, altho…

I've never seen nor heard of pointer_to ever being used to declare a pointer to something. I believe t's used inside of custom allocators for a generic type that might not use a normal pointer as the pointer type, but would never be used for normal declarations like this.

std::array is useful for letting the compiler avoid array-to-pointer decaying, value semantics, and also actually putting array length type info in a function parameter.

Re: The Clockwise/Spiral Rule of C declarations

#34

While cdecl was probably written before some of you where born it still does precisely one thing and does it well. Cdecl (and c++decl) is a program for encoding and decoding C (or C++) type declarations. http://linuxcommand.org/man_pages/cdecl1.html

Notably, one of the exercises in K&R (with a solution provided) is to write a mostly complete version of cdecl, which I think is great for dispelling much of the "magic" and increasing the understanding of how declarations are actually parsed.

That exercise was and probably still is above my pay grade.

Re: The Clockwise/Spiral Rule of C declarations

#35
Seems overly complex. The way I learned it, and now teach, is to read the type backwards (int const * is 'pointer to const int') for const correctness, but anything that requires more complex parsing by a human should just be typedef'd into submission.

Re: The Clockwise/Spiral Rule of C declarations

#36

While cdecl was probably written before some of you where born it still does precisely one thing and does it well. Cdecl (and c++decl) is a program for encoding and decoding C (or C++) type declarations. http://linuxcommand.org/man_pages/cdecl1.html

And online: http://cdecl.org/

Re: The Clockwise/Spiral Rule of C declarations

#37
The real rule is that the type construction operators mirror the unary and postfix family of operators (declaration follows use). For instance unary * declares a pointer, mimicking the dereference operator, and postfix [] and () declare functions, mimicking array indexing and function call.

To follow the declaration you make use of the fact that postfix operators in have a higher precedence than unary, and that of course unary operators are right-associative, whereas postfix are left-associative (necessarily so, since both have to "bind" with their operand).

So given

   int ***p[3][4][5];
we follow the higher precedence, in right to left associativity: [3], [4], [5]. Then we run out of that, and follow the lower-precedence * * * in right-to-left order.

If there are parentheses present, they split this process. We go through the postfixes, and then the unaries within the parens. Then we do the same outside those parens (perhaps inside the next level of parens):

   int ****(***p[3][4][5])[6][7];
               1 2  3  4
            765
                           8  9
       1111
       3210
Start at p, follow postfixes, then unaries within parens. Then the postfixes outside the parens and remaining unaries.

The result is in fact a spiral just from going root postfix unary out postfix unary out. We just don't have to focus on the spiral aspect of it.

Re: The Clockwise/Spiral Rule of C declarations

#38

Contrast the first example with Golang: str [10]*byte which reads exactly as it is declared: "str is an array of length 10 of pointers to byte" (byte is Go equivalent of C char (mostly)).

Or Rust:

    let string: [&u8; 10];
string is an array of references to unsigned integers of 8 bits of length ten

Re: The Clockwise/Spiral Rule of C declarations

#39

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)[restric…

What happened to the const in the second example?

The pointer itself is const; not what it points to.

    bar  -> const pointer to mutable array of ints
    *bar -> mutable array of ints
And const pointers are dereferenced with * , not (* const), so the rule needs an exception for const pointers (as well as volatile pointers).

Re: The Clockwise/Spiral Rule of C declarations

#40
post #38

Contrast the first example with Golang: str [10]*byte which reads exactly as it is declared: "str is an array of length 10 of pointers to byte" (byte is Go equivalent of C char (mostly)).

Or Rust: let string: [&u8; 10]; string is an array of references to unsigned integers of 8 bits of length ten

Actually it's a semicolon.
Post reply on HN