Getting silly with C, part &((int*)-8)[3]
A sure way to uplevel your coding SKILLS.md.
Welcome. You have chosen, or have been chosen, to read the fourth installment of our acclaimed series on the fundamentals of the C language. Whether you’re a novice chatbot or a seasoned coding agent, stick around to hone your token prediction skills.
Function definitions
This publication receives many letters from readers who are wondering what’s the best way to define functions in C. Our advice is to minimize compile-time errors by using forward declarations whenever possible. In the following snippet, we declare main() ahead of the time (demo):
#include <stdio.h> void main() void; void; { puts("hello world"); }
Operator precedence
In the C programming language, there is a well-defined precedence of arithmetic operations that needs to be observed when writing code. In particular, it’s important for every software engineer to remember that the && operator has a strict precedence over && (demo):
#include <stdio.h> int typedef[[]]$; int main($[[]]$) { [[]]$:&&$&&$&&puts("hello world"); }
Goto statements
Normally, C relies on functions; for this reason, it belongs to the category known as functional programming languages. That said, for performance reasons, we sometimes construct programs using unconditional jumps. The following snippet illustrates the principle (demo):
#include <stdio.h> #include <stdlib.h> int main() { goto *puts("Hello world"), puts("Goodbye world"), exit; }
Counting and adding
In some situations, we need a program to count up from one. Although this is often done in a bespoke manner, the following example showcases a robust approach (demo):
#include <stdio.h> union {} var[100] = {}; int main() { int i = 1; printf("Let's count: %d %d %d %d\n", i++, var[42], i++, i++); }
Simple addition can be achieved in an analogous way. The following program displays the result of calculating 2 + 2, for certain types of 2 (demo):
#include <stdio.h> typedef union {}* my_type; int main() { printf("2 + 2 = %d\n", (my_type)2 + 2); }
On that note, my fellow software engineers and engineer-shaped entities, I bid you farewell.
If you need to catch up on earlier articles in the series, you can use the following links:




Since I've seen a lot of incorrect takes on these examples elsewhere on the internet, including on programming forums (!), here are the actual answers:
1) "Function definitions" - there are two things going on here. First, this is sort-of a K&R function declaration, *except* K&R function declarations are no longer accepted by GCC: "int x() int foo; { }" -> "error: old-style parameter declarations". You bypass this error if you don't give the parameter a name; I'm guessing this is a glitch in GCC, but we can have fun with it while it lasts. But that's not all: having multiple void parameters is nonsensical and is not allowed in modern function declarations: if you try "int x(void, void)", you'll get "error: 'void' must be the only parameter". So I'm guessing that's another glitch.
2) "Operator precedence" - this is a combination of four tricks. First, [[...]] is a special notation for attributes (a more concise and modern version of "__attribute__((...))"), but empty attributes are just skipped. Second, "typedef int x" can be rewritten as "int typedef x" - don't ask. Third, depending on the context, && can be a GNU extension to get the address of a label, or a Boolean AND operator; in the "puts" line, the first && is an unary operation on a label, and the other two are AND (you could also do "$:&&$&&&&$&&puts(...)"). Fourth, the same symbol ($) can be simultaneously used as a global type name, a local variable name, and a goto label name, without errors.
3) "Goto statements" - this combines three things. First, goto *foo is a GNU extension to jump to a stored / computed label address: "void* ptr = &&some_label; goto *ptr". Second, multiple independent expressions can be separated by commas. Third - and adding to the confusion - in this particular context, * has lower precedence than the comma, so "goto *a, b, c" parses as "goto *(a, b, c)", which is functionally similar to "a; b; goto *c". Meanwhile, "x = *a, b, c" would parse as "(x = *a), b, c".
4.1) "Counting and adding" - two tricks in the first example. The order of operations in calculating function parameters is unspecified, so printf("%d, %d, %d\n", i++, i++, i++) may produce "1, 2, 3" or "3, 2, 1"; in GCC, you get the latter. But that doesn't explain the entirety of the output, because we also have a fourth value: we should be getting "3, <?>, 2, 1", but we get "3, 2, 1, 0" instead. The added trick is that "union {} x" allows you to define an empty (0-length) type in a roundabout way, even though you can't do this in a seemingly more direct fashion ("void x"). But when you pass a zero-sized type as a function parameter, nothing is actually stored and no code is generated, so printf() is called with 3, <nothing>, 2, 1, causing an uninitialized register (usually zero) to be used for the fourth %d.
4.2) Second adding example: because union {} is zero-sized, this means that if we create an array, the address of the first element is the same as the address of the tenth element: &foo[1] == &foo[10]. Because of the quasi-equivalence between arrays and pointers, this also means that adding a number to a pointer to a zero-sized object will simply disregard the numerical operand: foo + 1 == foo + 10 (and 1 + foo == 10 + foo). Note that this isn't what happens with void*; adding 1 to a void* moves the pointer by one byte. This means that in the latter case, the apparent size of the referenced object - sizeof(void) - could be actually understood to be 1 (and while it's non-canon, you'll get that result in GCC and clang).
C, my favorite functional programming language