Remix.run Logo
The C ``Clockwise/Spiral Rule''(c-faq.com)
37 points by etrvic 4 hours ago | 11 comments
pcfwik an hour ago | parent | next [-]

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 became hard to enjoy using statically typed languages that eschew it.

It is explained in more detail at this link: https://eigenstate.org/notes/c-decl

nitrix an hour ago | parent [-]

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.

gnramires 20 minutes ago | parent | prev | next [-]

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 declaration, when you declare 'char *ptr', which is a pointer to a char, you clearly can't read it exactly the same way ("char with content of a pointer"? More like, the content of a pointer is char). So maybe another symbol like @ (denoting "is a pointer"), or just the keyword pointer, might make things clearer, so you'd have 'char pointer ptr' (ptr is a pointer to a char, read backwards) or simply 'char @ ptr'. The shorter '@' would be justified when you have multiple pointer e.g. when working with multidimensional arrays (which are often @@@float, something like that). Just an idea that occurred me ;)

(Although I hadn't thought about pcfwik's principle that it's written as used, that makes somewhat more sense to me)*

Edit: Said otherwise, in usage syntax the convention (or at least my way of thinking) may be left-to-right, "content of" or "address of", while in declaration we read right-to-left, "is an int", or "is a pointer", and it would make sense to me that the symbol for "is a pointer" is different than the symbol for "content of"/"address of".

jandrese a minute ago | parent | next [-]

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.

dnautics 11 minutes ago | parent | prev [-]

i find Zig does it fairly well. there is also no ambiguity between pointer and multiply.

stephencanon 2 hours ago | parent | prev | next [-]

This comes up every so often, and while it's sort of almost true and attractive, it isn't actually correct.

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": https://cseweb.ucsd.edu/~gbournou/CSE131/rt_lt.rule.html).

jcranmer an hour ago | parent | next [-]

The best summary--and easiest to remember, IMHO--is that variables are declared as they are used.

Want to write an array of function pointers that return a pointer to an array of pointers to int? Well, that's:

  array ... -> x[N]
  ... of function pointers ... -> T (*x[N])()
  ... that return a pointer ... -> T *(*x[N])()
  ... to an array ... -> T (*(*x[N])())[M]
  ... of pointers ... -> T *(*(*x[N])())[M]
  ... to int ... -> int *(*(*x[N])())[M]
It doesn't make it all that easy to read, but you can at least write the complex types pretty reliably.

(The real answer is of course to just typedef every function pointer type or pointer-to-array and not worry about it anymore.)

mananaysiempre an hour ago | parent | prev [-]

Why would you do that? Ignoring for the moment arguments of prototypes and sizes of arrays, read C declarations the way they were designed: “char *a[<whatever>]” means that the expression *a[<whatever>] has type char; “char (*a(<whatever>))[<whatever>]” means that the expression (*a(<whatever>))[<whatever>] has type char; and so on. Then you apply the normal precedence rules for expressions, and in this case only knowing them for the prefix and postfix operators is sufficient. (Hint: all prefix operators have one precedence and all postfix ones another, and you know which one binds tighter if you know what *i++ means.)

classified an hour ago | parent | prev | next [-]

This is useful if you're far from the internet. Here's a web site that translates the gibberish to English or vice versa:

https://cdecl.org/

Or

  apt install cdecl
on Linux.
AlienRobot an hour ago | parent | prev | next [-]

This is why I like python.

str is a duck.

Joker_vD 2 hours ago | parent | prev [-]

> "str is an array 10 of pointers to char"

Wow, imagine if it was possible to actually use a language like that do declare the type of the variable like that? Something like

    str: array [0..9] of ^Char; 
or even

    var str [10]*uint8
Just imagine...