> The above example will print the value of a, but it won’t be initialized to 123!<p>It certainly could do though. In C, using an uninitialised variable does <i>not</i> mean "whatever that memory happened to have in it before" (although that is a potential result). Instead, it's undefined behaviour, so the compiler can do what it likes.<p>For example, it could well unconditionally initialise that memory to 123. Alternatively, it could notice that the whole snippet has undefined behaviour so simply replace it with no instructions, so it doesn't print anything at all. It could even optimise away the return that presumably follows that code in a function, so it ends up crashing or doing something random. It could even optimise away the instructions <i>before</i> that snippet, if it can prove that they would only be executed if followed by undefined behaviour – essentially the undefined behaviour can travel back in time!
This features the construct<p><pre><code> switch(k) {
if (0) case 0: x = 1;
if (0) case 1: x = 2;
if (0) default: x = 3;
}
</code></pre>
which is a switch where you don't have to write break at the end of every clause.<p><pre><code> #define brkcase if (0) case
</code></pre>
That might be worth using. Compilers won't love the control flow but they'll probably delete it effectively.
This can be used to implement coroutines in C. <a href="https://stackoverflow.com/questions/24202890/switch-based-coroutines" rel="nofollow">https://stackoverflow.com/questions/24202890/switch-based-co...</a>
Why did I not know that this:<p><pre><code> case 1 ... 10:
</code></pre>
Is valid C? I have been programming in C for years, what standard is this from?
This reminds me of some silly C code I once wrote for fun, which counts down from 10 to 1:<p><pre><code> #include <stdio.h> // compile & run: gcc -Wall countdown.c -o countdown && ./countdown
int n = 10; int main(int argc, char *argv[]) { printf("%d\n", n) && --n && main(n, NULL); }
</code></pre>
Python version:<p><pre><code> import sys # run: python3 countdown.py 10
def main(n:int): sys.stdout.write(f"{n}\n") and n-1 and main(n-1)
main(int(sys.argv[1]))
</code></pre>
Shell version:<p><pre><code> # run ./countdown.sh 10
echo $1 && (($1-1)) && $0 $(($1-1))</code></pre>
The final obfuscated code snippet in the article brought to light another GCC extension:<p><a href="https://stackoverflow.com/questions/34559705/ternary-conditional-operator-without-the-middle-expression" rel="nofollow">https://stackoverflow.com/questions/34559705/ternary-conditi...</a>
Found these silly tricks by the author of this blog on twitter first. Switch statement can do loops too <a href="https://twitter.com/lcamtuf/status/1807129116980007037" rel="nofollow">https://twitter.com/lcamtuf/status/1807129116980007037</a>
Due to the way lifetimes work in C (they begin with the block, not the declaration), the following is legal:<p><pre><code> #include <stdio.h>
#include <stddef.h>
int main()
{
{
int *p = NULL;
if (p)
{
what:
printf("a = %d\n", *p);
return 0;
}
int a = 123;
p = &a;
goto what;
}
}</code></pre>
> switch (i) case 1: puts("i = 1");<p>I've seen this in the wild, particularly with macros.<p><pre><code> #define assert(c) if (!c) ...
if (foo) assert(...);
else bar(); // oops!</code></pre>
<i>Fun at parties alert:</i><p>Let's stop getting silly with C, too many CVEs!<p>---<p><i>Serious comment:</i><p>It's a rather cool article actually. Not something I'd do daily but it's kind of sort of useful to know these techniques.
see also <a href="https://www.chiark.greenend.org.uk/~sgtatham/mp/" rel="nofollow">https://www.chiark.greenend.org.uk/~sgtatham/mp/</a><p>Metaprogramming custom control structures in C by Simon Tatham
If only there was a way of using setjmp/longjmp-style contexts instead of goto, un/winding the stack as required. So we could travel around in time... unfortunately you can't work with a setjmp buffer before it's actually created, unlike gotos.
My undergrad was entirely in the C language and I’m very glad for it. Sometimes more modern languages can throw me for a loop, no pun intended, but the beauty (and horror) of C is that you are pretty close to the metal, it’s not very abstracted at all, and it allows you a lot of freedom (which is why it’s so foot gunny).<p>I will never love anything as much as I love C, but C development jobs lie in really weird fields I’m not interested in, and I’m fairly certain I am not talented enough. I have seen C wizardry up close that I know I simply cannot do. However, one of the more useful exercises I ever did was implement basic things like a file system, command line utilities like ls/mkdir etc. Sometimes they are surprisingly complex, sometimes no.<p>After you program in C for a while certain conventions meant to be <i>extra</i> careful kind of bubble up in languages in a way that seems weird to other people. for example I knew a guy that’d auto reject C PR’s if they didn’t use the syntax if (1==x) rather than if (x==1). The former will not compile if you accidentally use variable assignment instead of equality operator (which everyone has done at some point).<p>This tendency bites me a lot in some programming cultures, people (ime) tend to find this style of programming as overly defensive.