4 ms·
That line is honestly trivial for anyone familiar with the spiral rule. Interpret p as a pointer to an array of pointers to integer. Since arrays decay to poin
by bruce343434 5y ago
That line is honestly trivial for anyone familiar with the spiral rule.
Interpret p as a pointer to an array of pointers to integer. Since arrays decay to pointers, you triple dereference it to get
1. the array
2. the first element of that array
3. whatever int that element is pointing to
Then set it to 1.
It took me about 5 seconds. I have no idea what that Zig is supposed to do on the other hand.
Note: the programmer who wrote that C code was being very obtuse. This is essentially just `*a = 1`, the whole p thing is some form of baroque code.
- nicoburns 5y ago> That line is honestly trivial for anyone familiar with the spiral rule. Perhaps, but the spiral rule is one of the most complicated parts of C. Most things are trivial to someone who knows how to do the thing.
- bruce343434 5y agoYou're right of course. Every language has warts, and declaration, especially involving pointers, is C's. But once you internalize that declaration mirrors usage, together with the spiral rule, it will all immediately become clear. There is some method to the madness.
- dnautics 5y agoI thought the spiral rule was a tongue-in-cheek "rule" that was just made to point out a problem with C.
- deleted 5y ago[deleted]
- klyrs 5y agoIf it's not wrong...
- rightbyte 5y agoThe confusing part for beginners is maybe to know were to start the spiral (or right left scheme) in a cast since there is no identifier.
- bruce343434 5y agoThat's a fair point. In C, you always start at the identifier. In case there is none, type declarations can contain parentheses*, and just like in math, parens resolve first, so it's from innner to outer. So in this case one starts with the `(*)`. * the tricky part is that `()` are also used to denote functions. So yeah, it's not always readable. `(*)()` would be a pointer to a function returning int (the default type) and taking an unspecified amount of arguments.
- steerablesafe 5y agoYeah, because the "spiral-rule" is dumb, it's not a spiral at all. It's just regular operator precedence.
- abainbridge 5y agoI've always thought that the "obfuscated C competition" is proof that it is quite hard to obfuscate C. There'd be no challenge in most other languages. (I haven't got enough knowledge of Zig to know if it is as easy to understand as C in general).
- bruce343434 5y agoYes, the IOCCC is always so interesting. Because the language is so spartan, there's often only one or two ways to do something, because to solve problems on a higher level you have to build the scaffolding yourself. Whereas in the usual dynamic scripting language you can reuse variables as different types, and conjure up a really convoluted approach in just a few statements.
- tyingq 5y agoA fair amount of obfuscated C is also the macro processor.
- bruce343434 5y agoI feel lately every IOCCC submission just has an obligatory "replace some part of the code with arbitrary identifiers which the preprocessor will search and replace back". Honestly a cheap gimmick, but it leads to a bigger "whut" factor when first seeing the code.
- palotasb 5y agoAny time the spiral rule comes up, I like to point out that it's wrong. It is instructive in a way because one learns more about C declaration syntax, but it is even more instructive to recognize why it is wrong. The spiral rule works only if there is no pointer to pointer or array of array in the type. But take this for example: +----------------------------+ | +-----------------------+ | | | +------------------+ | | | | | +-------------+ | | | | | | | +--------+ | | | | | | | | | +--+ | | | | | | | | | | ^ | | | | | | int * * ¦ ¦ ¦ xxx[1][2][3] | | | ^ | | | | | | | | | | | | | | | | +-----+ | | | | | | | | | +----------+ | | | | | | | +---------------+ | | | | | ---------------------+ | | | +-------------------------+ | +-------------------------------+ The type of xxx is a [1-element] array of [2-element] array of [3-element] array of pointer to pointer to ints. I drew a spiral that passes through each specifier in the correct order. Notice that to make the spiral correct it has to skip the pointer specifiers in the first three loops. This is marked by ¦. This is not mentioned in the original spiral rules and one could be forgiven to parse the expression as xxx -> [1] -> pointer -> [2] -> etc. following a spiral that doesn't skip the pointers. The Right-Left Rule is quoted less frequently on HN but it's a correct algorithm for deciphering C types: http://cseweb.ucsd.edu/~ricko/rt_lt.rule.html http://cseweb.ucsd.edu/~ricko/rt_lt.rule.html The spiral rule can be modified to process all array specifiers before all pointer specifiers, but then you'd have to specify that the order to do so is right and then left. At that point it's just the Right-Left Rule.
- userbinator 5y agoThe summary is "array and function has higher precedence than pointer, and parentheses override all". This explains why int *f(); is a function returning pointer to int, while int (*f)() is a pointer to function returning int. Likewise, int *a[] is an array of pointer to int, while int (*a)[] is a pointer to array of int.
- pmarreck 5y agobetween things like this and the pre- and post-increment/decrement operators, it's shocking that C code generates so many security holes ;)
- simias 5y agoDo the parens in `int * (*) []` do anything? When I saw them I immediately assumed that function pointers would be involved, but it doesn't seem to be the case and now I'm confused. EDIT: Uh, apparently you can add parens to casts but not declarations. This doesn't compile: int (*) p = (int *)&i; This does: int *p = (int (*))&i I can't quite justify this behaviour.
- MauranKilom 5y agoI think this is just about where the identifier has to go in a declaration. Otherwise the spiral rule [or rather right-left-rule, as pointed out elsethred] doesn't start at the right place (to over-simplify it). You can in fact have parentheses in declarations, but the identifier must be on the inside, not just to the right of everything: https://godbolt.org/z/vKzcYMdvK https://godbolt.org/z/vKzcYMdvK
- foxfluff 5y agoYes they do something. They are used to override precedence, just as you would in a math expression. Array indexing has higher precedence than pointer dereferencing, and declarations follow the same precedence that you have in expressions. int **p[123]; // p is array(123) of pointer to pointer to int int *(*p)[123]; // p is a pointer to array(123) of pointer to int Sometimes people find casts confusing because there is no identifier inside. But you can easily read it if you know where the identifier would be in an equivalent declaration. Precedence is usually documented in a man page called operator. http://man.openbsd.org/operator http://man.openbsd.org/operator
- Joker_vD 5y agoBut &a gives you "int * *", and then it's dereferenced three times. How?
- nyc_pizzadev 5y agoThere is an array cast [] in there. So dereferencing that using a plain * takes you back to the original double pointer.
- deleted 5y ago[deleted]
- deleted 5y ago[deleted]
- cecilpl2 5y agoThe int** is not dereferenced three times. The cast says "treat this int* as an array of int*, then the first dereference says "give me the first element of that array", which gets you back the original int*.
- Joker_vD 5y agoSee, this would be a better, shorter, and more insightful explanation than that an article full of mechanical transformations performed by hand. "We start with a pointer-to-int called "a", take pointer to it and name it "p" (it's a pointer-to-pointer-to-int, although we store it in a pointer-to-whatever variable), then cast it to some weird type, dereference it thrice and store 1 into the resulting target. Two questions remain: a) what is that weird type? b) we have two levels of indirection but three dereferences, how does it work? The answer to the first question is that weird type is "pointer-to-array-of-pointers-to-int", and it helps us to answer the second question: dereferencing a value of that type is a no-op in arithmetical sense (but has a type-casting effect)."
- eMSF 5y agoUnless I'm mistaken, this is almost, but not quite correct. (Firstly, what's being cast is `p` which is a `void-pointer`.) More importantly, the latter cast says to treat p as a `pointer to an array of int-pointers`, which means that the first dereference actually gives you the array. Expressions with an array type in most contexts (including this one) decay into pointers to their first elements – thus the second dereference gets you the first element of the array, an int-pointer, and the third one gets you the integer being pointed to. Also, because this array type is incomplete (it has no size), I don't think you could use it in any context where it didn't "decay" into a pointer to its first element (you can test what your compiler says if you try to measure the array's size with the sizeof operator when only using one dereference).
- userbinator 5y agoThe spiral rule is incorrect in the general case as other comments have already pointed out (the correct way is to read in precedence order) but I agree that it was trivial to decipher. I guess the opposite could be true for an experienced Zig programmer.