June 30th, 2024

Weekend projects: getting silly with C

The C programming language's simplicity and expressiveness, despite quirks, influence other languages. Unconventional code structures showcase creativity and flexibility, promoting unique coding practices. Subscription for related content is encouraged.

Read original articleLink Icon
Weekend projects: getting silly with C

The article discusses the unique characteristics of the C programming language, highlighting its simplicity and expressiveness despite its quirks. It mentions how C's syntax has influenced other programming languages and led to the emergence of code obfuscation as an art form. The piece showcases examples of unconventional code structures in C, such as using the && operator for label addressing and implementing loops within variable declarations using labels. While these coding practices may not be universally safe or portable, they demonstrate the flexibility and creativity that C allows for in programming. The article also encourages readers to subscribe for more content on similar topics.

Related

Laziness is the source of Innovation and Creativity

Laziness is the source of Innovation and Creativity

Laziness can spur innovation in programming by encouraging efficiency and problem-solving. Embracing laziness responsibly can lead to creative and efficient solutions, promoting a balance between productivity and creativity.

As you learn Forth, it learns from you (1981)

As you learn Forth, it learns from you (1981)

The Forth programming language is highlighted for its unique features like extensibility, speed, and efficiency. Contrasted with Basic, Forth's threaded code system and data handling methods make it versatile.

Programming Like It's 1977

Programming Like It's 1977

The article explores programming games on the Atari VCS, a pioneering hardware platform from the 1970s with constraints that inspired creativity. Coding in 6502 assembly language offers a retro experience. The Atari 2600+ release supports old hardware for modern gaming. Learning on the Atari VCS reveals early programmers' challenges and solutions, fostering creativity.

The C Standard charter was updated, now with security principles as well

The C Standard charter was updated, now with security principles as well

The ISO/IEC JTC1/SC22/WG14 committee oversees C Standard development, focusing on portability, efficiency, and stability. Collaboration with the C++ committee ensures compatibility. Principles guide feature integration, code efficiency, security, and adaptability.

Beyond monospace: the search for the perfect coding font

Beyond monospace: the search for the perfect coding font

Designing coding fonts involves more than monospacing. Key considerations include hyphens resembling minus signs, aligning symbols, distinguishing zero from O, and ensuring clarity for developers and type designers. Testing with proofing strings is recommended.

Link Icon 17 comments
By @quietbritishjim - 5 months
> The above example will print the value of a, but it won’t be initialized to 123!

It certainly could do though. In C, using an uninitialised variable does not 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.

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 before 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!

By @JonChesterfield - 5 months
This features the construct

  switch(k) {
    if (0) case 0: x = 1;
    if (0) case 1: x = 2;
    if (0) default: x = 3;
  }
which is a switch where you don't have to write break at the end of every clause.

  #define brkcase if (0) case
That might be worth using. Compilers won't love the control flow but they'll probably delete it effectively.
By @geon - 5 months
This can be used to implement coroutines in C. https://stackoverflow.com/questions/24202890/switch-based-co...
By @nj5rq - 5 months
Why did I not know that this:

    case 1 ... 10:
Is valid C? I have been programming in C for years, what standard is this from?
By @jftuga - 5 months
This reminds me of some silly C code I once wrote for fun, which counts down from 10 to 1:

    #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); }
Python version:

    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]))
Shell version:

    # run ./countdown.sh 10
    echo $1 && (($1-1)) && $0 $(($1-1))
By @teo_zero - 5 months
Another source of surprise:

  4[arr] // same as arr[4]
By @codext - 5 months
The final obfuscated code snippet in the article brought to light another GCC extension:

https://stackoverflow.com/questions/34559705/ternary-conditi...

By @smusamashah - 5 months
Found these silly tricks by the author of this blog on twitter first. Switch statement can do loops too https://twitter.com/lcamtuf/status/1807129116980007037
By @mgaunard - 5 months
aren't the switch shenanigans important to the duff's device?
By @o11c - 5 months
Due to the way lifetimes work in C (they begin with the block, not the declaration), the following is legal:

  #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;
      }
  }
By @junon - 5 months
> switch (i) case 1: puts("i = 1");

I've seen this in the wild, particularly with macros.

    #define assert(c) if (!c) ...
    
    if (foo) assert(...);
    else bar(); // oops!
By @pdimitar - 5 months
Fun at parties alert:

Let's stop getting silly with C, too many CVEs!

---

Serious comment:

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.

By @drzzhan - 5 months
I am so lost at the final block of code. Does every C developer have to deal with this everyday?
By @fanf2 - 5 months
see also https://www.chiark.greenend.org.uk/~sgtatham/mp/

Metaprogramming custom control structures in C by Simon Tatham

By @nxobject - 5 months
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.
By @JohnMakin - 5 months
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).

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.

After you program in C for a while certain conventions meant to be extra 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).

This tendency bites me a lot in some programming cultures, people (ime) tend to find this style of programming as overly defensive.