I just saw a picture today and think I'd appreciate explanations. So here is the picture:
I found this confusing and wondered if such codes are ever practical. I googled the picture and found another picture in this reddit entry, and here is that picture:
So this "reading spirally" is something valid? Is this how C compilers parse?
It'd be great if there are simpler explanations for this weird code.
Apart from all, can these kind of codes be useful? If so, where and when?
There is a question about "spiral rule", but I'm not just asking about how it's applied or how expressions are read with that rule. I'm questioning usage of such expressions and spiral rule's validity as well. Regarding these, some nice answers are already posted.
I happen to be the original author of the spiral rule that I wrote oh so many years ago (when I had a lot of hair :) and was honored when it was added to the cfaq.
I wrote the spiral rule as a way to make it easier for my students and colleagues to read the C declarations "in their head"; i.e., without having to use software tools like cdecl.org, etc. It was never my intent to declare that the spiral rule be the canonical way to parse C expressions. I am though, delighted to see that the rule has helped literally thousands of C programming students and practitioners over the years!
For the record,
It has been "correctly" identified numerous times on many sites, including by Linus Torvalds (someone whom I respect immensely), that there are situations where my spiral rule "breaks down". The most common being:
As pointed out by others in this thread, the rule could be updated to say that when you encounter arrays, simply consume all the indexes as if written like:
Now, following the spiral rule, I would get:
"ar is a 10x10 two-dimensional array of pointers to char"
I hope the spiral rule carries on its usefulness in learning C!
P.S.:
I love the "C isn't hard" image :)
The "spiral" rule kind of falls out of the following precedence rules:
The subscript
[]
and function call()
operators have higher precedence than unary*
, so*f()
is parsed as*(f())
and*a[]
is parsed as*(a[])
.So if you want a pointer to an array or a pointer to a function, then you need to explicitly group the
*
with the identifier, as in(*a)[]
or(*f)()
.Then you realize that
a
andf
can be more complicated expressions than just identifiers; inT (*a)[N]
,a
could be a simple identifier, or it could be a function call like(*f())[N]
(a
->f()
), or it could be an array like(*p[M])[N]
, (a
->p[M]
), or it could be an array of pointers to functions like(*(*p[M])())[N]
(a
->(*p[M])()
), etc.It would be nice if the indirection operator
*
was postfix instead of unary, which would make declarations somewhat easier to read from left to right (void f[]*()*();
definitely flows better thanvoid (*(*f[])())()
), but it's not.When you come across a hairy declaration like that, start by finding the leftmost identifier and apply the precedence rules above, recursively applying them to any function parameters:
The
signal
function in the standard library is probably the type specimen for this kind of insanity:At this point most people say "use typedefs", which is certainly an option:
But...
How would you use
f
in an expression? You know it's an array of pointers, but how do you use it to execute the correct function? You have to go over the typedefs and puzzle out the correct syntax. By contrast, the "naked" version is pretty eyestabby, but it tells you exactly how to usef
in an expression (namely,(*(*f[i])())();
, assuming neither function takes arguments).Regarding the usefulness of this, when working with shellcode you see this construct a lot:
While not quite as syntactically complicated, this particular pattern comes up a lot.
More complete example in this SO question.
So while the usefulness to the extent in the original picture is questionable (I would suggest that any production code should be drastically simplified), there are some syntactical constructs that do come up quite a bit.
There is a rule called the "Clockwise/Spiral Rule" to help find the meaning of a complex declaration.
From c-faq:
You can check the link above for examples.
Also note that to help you there is also a website called:
http://www.cdecl.org
You can enter a C declaration and it will give its english meaning. For
it outputs:
EDIT:
As pointed out in the comments by Random832, the spiral rule does not address array of arrays and will lead to a wrong result in (most of) those declarations. For example for
int **x[1][2];
the spiral rule ignores the fact that[]
has higher precedence over*
.When in front of array of arrays, one can first add explicit parentheses before applying the spiral rule. For example:
int **x[1][2];
is the same asint **(x[1][2]);
(also valid C) due to precedence and the spiral rule then correctly reads it as "x is an array 1 of array 2 of pointer to pointer to int" which is the correct english declaration.Note that this issue has also been covered in this answer by James Kanze (pointed out by haccks in the comments).
The declaration
is just an obscure way of saying
with
In practice, more descriptive names will be needed instead of ResultFunction and Function. If possible I would also specify the parameter lists as
void
.Applying spiral rule or using cdecl are not valid always. Both fails in some cases. Spiral rule works for many cases, but it is not universal.
To decipher complex declarations remember these two simple rules:
Always read declarations from inside out: Start from innermost, if any, parenthesis. Locate the identifier that's being declared, and start deciphering the declaration from there.
When there is a choice, always favour
[]
and()
over*
: If*
precedes the identifier and[]
follows it, the identifier represents an array, not a pointer. Likewise, if*
precedes the identifier and()
follows it, the identifier represents a function, not a pointer. (Parentheses can always be used to override the normal priority of[]
and()
over*
.)This rule actually involves zigzagging from one side of the identifier to the other.
Now deciphering a simple declaration
Applying rule:
Let's decipher the complex declaration like
by applying the above rules:
Here is a GIF demonstrating how you go (click on image for larger view):
The rules mentioned here is taken from the book C Programming A Modern Approach by K.N KING.