Live data from Hacker News

Clockwise/Spiral Rule (1994)

c-faq.com

21–30 of 59 posts

Re: Clockwise/Spiral Rule (1994)

#21
post #17
post #10

Earlier quoted context omitted.

Some more examples: int v, *w, x[5], *y[5], (*z[5])(int, int); Where v is an int, w is a pointer, x is an array, y is an array of pointers, z is an array of function pointers, etc. Similarly, typedef is also just a keyword in front of a regular declaration. int foo[5]; typedef int foo[5]; int bar(void); typedef int bar(void); Now you can use `bar *` as a function pointer. The entire language works like this.

The way I've learned to read it is int v; means that `v` is an `int`. int *w; means that `*w` is an `int`, meaning `w` is a pointer to an `int`. int *y[5] (note that `◌[]` has higher precedence than `*◌`, so this is `*(y[5])`) means that `*y[5]` is an `int`, so `y[5]` is a pointer to an `int`, meaning `y` is an array of `int` pointers. int (*(*kitchensink[5])(int, int))[6]; means that `(*(*kitchensink[5])(int, int))[…

> int *w;

But some misguided style guides demand the misleading `int* w;`, and then act surprised by `int* w, x`;

Re: Clockwise/Spiral Rule (1994)

#22

I've written some C code recently, and it came to me perhaps the pointer syntax may not be ideal. I'm not sure what the ideal would be, but I think a different notation for usage and declaration could make it less confusion. In particular I associate '*' (used as *ptr, i.e. content that ptr points to), with content, as opposed to '&' (from &var, address of var), so again '*' means content thing points to. But in decl…

The reading of `char *p` is: The following things are `char`: `*p`.

Re: Clockwise/Spiral Rule (1994)

#23
post #4

Being taught this rule in undergrad really hampered my appreciation of C. As I've said in a previous comment, the real key that unlocked understanding C declarations for me is the mantra "declaration follows use." You declare a variable in C in exactly the same way you would use it: if you know how to use a variable, then you know how to read and write a declaration for it. Once I understood this elegant idea, it bec…

Call me a hater but I don't like the spiral rule and I like "declaration follows use" even less. How do you make an std::array of a given type? Wrap the existing type in an extra layer of std::array, we all know this, it makes sense, there's no reasonable alternative. How do you make a C-array of a given type? Oh boy, "prepend the array specifier before the list of existing array specifiers" (actually it's worse beca…

>How do you make an std::array of a given type? Wrap the existing type in an extra layer of std::array, we all know this

huh? where is the extra layer?

    std::array array_of_ints = { 1, 2, 3, 4, 5 };
>How do you make a C-array of a given type? Oh boy, "prepend the array specifier before the list of existing array specifiers"

?

    int c_style_array[5] = {2, 3, 5, 7, 11};

Re: Clockwise/Spiral Rule (1994)

#26

Earlier quoted context omitted.

Call me a hater but I don't like the spiral rule and I like "declaration follows use" even less. How do you make an std::array of a given type? Wrap the existing type in an extra layer of std::array, we all know this, it makes sense, there's no reasonable alternative. How do you make a C-array of a given type? Oh boy, "prepend the array specifier before the list of existing array specifiers" (actually it's worse beca…

>How do you make an std::array of a given type? Wrap the existing type in an extra layer of std::array, we all know this huh? where is the extra layer? std::array array_of_ints = { 1, 2, 3, 4, 5 }; >How do you make a C-array of a given type? Oh boy, "prepend the array specifier before the list of existing array specifiers" ? int c_style_array[5] = {2, 3, 5, 7, 11};

I assume he means multi-dimensional arrays:

    std::array, 5> array_of_ints;
    int c_style_array[5][3];
But the C-style array is more readable, so I am not sure what the complaint really is about.

Re: Clockwise/Spiral Rule (1994)

#27
post #4

Being taught this rule in undergrad really hampered my appreciation of C. As I've said in a previous comment, the real key that unlocked understanding C declarations for me is the mantra "declaration follows use." You declare a variable in C in exactly the same way you would use it: if you know how to use a variable, then you know how to read and write a declaration for it. Once I understood this elegant idea, it bec…

Call me a hater but I don't like the spiral rule and I like "declaration follows use" even less. How do you make an std::array of a given type? Wrap the existing type in an extra layer of std::array, we all know this, it makes sense, there's no reasonable alternative. How do you make a C-array of a given type? Oh boy, "prepend the array specifier before the list of existing array specifiers" (actually it's worse beca…

> we all know this, it makes sense, there's no reasonable alternative

There absolutely are reasonable alternate ways to represent ordered data that don't involve templates. The way that C does it makes sense in most cases, and if you are looking at something that you cannot understand, you are looking at bad code.

> "Declaration follows use" immediately goes out the window when faced with typedeffed types being used as the base type

Typedefs are an abstraction. If you create a typedef, it is usually because you only want to handle the data as a whole, passing it to helper functions that remove the typedef. Also, declaration of use does not break down with typedefs:

    typedef char *(*fn)(int, char *);
    fn my_fn;

    char *s = (*my_fn)(0, ""); // Proper use
> "Declaration follows use" is the type level equivalent of taking off your socks before taking off your shoes because that's the order in which you put them on.

Please give me an example of some C code where this is the case.

Re: Clockwise/Spiral Rule (1994)

#28

I've written some C code recently, and it came to me perhaps the pointer syntax may not be ideal. I'm not sure what the ideal would be, but I think a different notation for usage and declaration could make it less confusion. In particular I associate '*' (used as *ptr, i.e. content that ptr points to), with content, as opposed to '&' (from &var, address of var), so again '*' means content thing points to. But in decl…

I thought it was a bit of a miss that C didn't use ^ as the pointer sigil. I mean it's literally pointing. I'm guessing some early terminals didn't have that character on the keyboard.

> I thought it was a bit of a miss that C didn't use ^ as the pointer sigil. I mean it's literally pointing. I'm guessing some early terminals didn't have that character on the keyboard.

That can't be the reason; IIRC Pascal has always used '^' for pointer derefencing (just like git's HEAD^^^)

Re: Clockwise/Spiral Rule (1994)

#29
post #21
post #17

Earlier quoted context omitted.

The way I've learned to read it is int v; means that `v` is an `int`. int *w; means that `*w` is an `int`, meaning `w` is a pointer to an `int`. int *y[5] (note that `◌[]` has higher precedence than `*◌`, so this is `*(y[5])`) means that `*y[5]` is an `int`, so `y[5]` is a pointer to an `int`, meaning `y` is an array of `int` pointers. int (*(*kitchensink[5])(int, int))[6]; means that `(*(*kitchensink[5])(int, int))[…

> int *w; But some misguided style guides demand the misleading `int* w;`, and then act surprised by `int* w, x`;

I split the difference and write it "int*w".

(Most style guides tell you to declare one name per line anyway...)

Re: Clockwise/Spiral Rule (1994)

#30
There is only one iteration through the spiral in any one declarator unless there are parentheses. This is because it's basically:

   [pre] [pre] ... [pre] [core] [post] ... [post]
We have the core of the declarator, usually a name (or empty when omitted). On the left there is a clump of zero or more prefix declarative operators like the pointer *, and on the right postfix ones, like array and function parentheses.

No matter how many there are, you only to around he spiral one time. Let's add the declaration specifiers:

   [spec] ... [spec] [pre] [pre] ... [pre] [core] [post] ... [post]
                                           ------
"We declare "core" to be ..."

  [spec] ... [spec] [pre] [pre] ... [pre] [core] [post] ... [post]
                                          ------      ^
                                             \_______/

                                   
"We declare "core" to be a this, that and other postfix thing ..."

                             ________________________
                             /                        \
  [spec] ... [spec] [pre] [pre] ... [pre] [core] [post] ... [post]
                                           ------      ^
                                              \_______/

"We declare "core"t to be a this, that and other postfix thing, of this pre, pre ...

                              ________________________
                             /                        \
  [spec] ... [spec] [pre] [pre] ... [pre] [core] [post] ... [post]
                  ^________/               ------      ^
                                              \_______/

"We declare "core"t to be a this, that and other postfix thing, of this pre, pre of type/quality given by specs."

For instance:

   const unsigned int * * * x [][][3]
  
"Declare x to be a an array of arrays of arrays of 3 pointers to pointers to pointers, to const unsigned int"

But parentheses override the precedence of postfix versus prefix, so that's when the path follows a spiral with multiple loops, for each nesting level:

    const unsigned int *(*(*x)[][])[3]
Without parentheses, the precedence is as if implicitly there were these parentheses:

    const unsigned int ***(x[][][3])
i.e. postfix "binds tighter" than prefix/unary. That's the whole basis for the spiral: flipping from left to right chasing the sequences of postfix and unary operators, though all the levels of parentheses.

BTW, as a matter of terminology, ISO C does not call type construction punctuators operators; only expressions have operators. In computer science terminology related to programming languages, C declarators are type constructing expressions in which the elements like [] and * are type constructing operators.

Post reply on HN