Back to News
Advertisement
Advertisement

⚡ Community Insights

Discussion Sentiment

0% Positive

Analyzed from 430 words in the discussion.

Trending Topics

#amp#int#puts#address#void#world#typedef#argument#function#exit

Discussion (3 Comments)Read Original on HackerNews

pkastingabout 2 hours ago
The common theme of this whole series of articles is basically "GCC extensions allow abominations".
ueckerabout 1 hour ago
I don't think this is really about GCC extensions (or obsolete language modes) rather than using the flexibility of the syntax to write things in a confusing way.

The first is simply K&R definitions + strange formatting.

nneonneo29 minutes ago
Some of the previous editions came with explainers, but this one doesn't, so here's what's going on:

`void main() void;`: Despite sort of looking like an independent declaration (which threw me for a loop initially), this is actually an old-style C function definition in disguise. This style of function definition looks like `int foo(a, b, c) int a; int b; int c; { return a * b + c; }`. Without the misleading spacing, the blog code becomes `void main() void; void; { puts("hello world"); }`, in which it is clear that this is an old-style function definition with two useless declarations attached (void;).

`int typedef[[]]$;`: This is a combination of a few features: (1) $ is a legal identifier in C, (2) [[]] is an empty C23 attribute specifier sequence (basically a standardized form of __attribute__), (3) && is both the familiar binary operator, and a unary operator that takes the address of a label, and (4) `typedef` doesn't have to be the first keyword in a typedef definition. So, `int typedef[[]]$;` is simply `typedef int $;` - defining $ as a type alias for int. `int main($[[]]$)` says that `main` takes one argument - an `int` (typedef'd $) called `$`. `[[]]$:&&$&&$&&puts("hello world");` defines a label called `$`, then breaks down as `&&$ && $ && puts(...)` - take the address of the label `$`, logical AND the argument `$`, logical AND the result of `puts`.

`goto *puts("Hello world"), puts("Goodbye world"), exit;`: this is a computed goto statement that evaluates the comma-expression `puts("Hello world"), puts("Goodbye world"), exit`, which calls `puts` twice and produces the address of `exit` (implicit function-to-function-pointer conversion), using that as the goto target. So, in effect, it's two free calls to puts followed by `goto *&exit`. Note that this is UB, as it effectively tail-calls `exit(int)` with no arguments.

`printf("Let's count: %d %d %d %d\n", i++, var[42], i++, i++);`: As far as I can tell, this is a bit of compiler weirdness (and is extremely UB). `var` is an array of empty unions, and takes up no space at all (although it will still have a defined address in the binary). `var[42]` is an empty union object and, at least on x86-64, takes up no space in a variadic argument list; it's therefore skipped over entirely when building the arguments to `printf`. This means that `printf` actually gets one fewer argument than expected, which just-so-happens to be a zero on the Compiler Explorer demo. Because this one is very UB (both because of the unsequenced `i++` and the incorrect argument list), expect the result to vary depending on the mood your compiler is in.

`(my_type)2 + 2`: `my_type` is a pointer to an anonymous empty union with `sizeof` 0, so `(my_type)2` effectively treats 2 as the base address to an array of 0-sized unions. Adding a number to a pointer is equivalent to taking an array address (i.e. `p + x == &p[x]`). Since the size is zero, asking for the address of element 2 is still going to give the base address (2). Compare this with, for example, `(int *)2 + 2` (gives 10, assuming `int` is four bytes in size). Note though that there is again UB here, because the argument to `printf` is a pointer, but it's being printed using the `%d` specifier.