1. 28
  1. 5

    I’m pretty sure the first one is wrong. I thought the spec said explicitly that evaluating sizeof may not have side effects. This construct seems to be accepted by GCC and Clang but I think it’s actually UB. In C++ decltype may not have side effects and I believe this follows from the same rule.

    VLA typedefs are useful for the same reason that VLAs are useful. If you are using a VLA, it’s useful to be able to name the type.

    Array designators were a GNU extension but were added to C in C11, I think. They’re really useful and it’s a shame that they’re not in C++.

    The preprocessor is a functional language and you can pass a macro to another macro, but evaluation may not happen when you think it does. The most common case where this bites people is in stringify macros.

    The switch thing comes with the same caveat as normal switch: It’s basically goto and you must be really careful not to branch over variable initialisation. I can’t remember if C rejects constructs that do or just makes them UB. It also doesn’t generate better code with a modern compiler than writing it the sane way - it’s just basic blocks and branches at the IR layer.

    The a[b] thing is actually even more fun. Addition in C is commutative, so a+b is equivalent to b+a in all contexts. Pointer + integer is defined as address + (size of pointee) * integer and because addition is commutative this doesn’t matter if you put the pointer on the left or right side. The same rule is why integer promotion works in C. If I were trying to design a language feature that looked sensible at first glance but would lead to many bugs, this is probably about as good as anything I could come up with.

    1. 4

      I thought the spec said explicitly that evaluating sizeof may not have side effects.

      Here’s what it says in section paragragh 2 (about the sizeof and _Alignof operators) in my copy of the C11 spec:

      If the type of the operand is a variable length array type, the operand is evaluated;

      Then in section paragraph 5 (Array declarators):

      If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall have a value greater than zero.

      I can’t find any mention of side-effects in array declarator expressions.

      1. 2

        Array designators were a GNU extension but were added to C in C11, I think. They’re really useful and it’s a shame that they’re not in C++.

        They were introduced in C99 at the same time as member designators (https://port70.net/~nsz/c/c99/n1256.html#6.7.8p6). The obsolete GNU syntax for designators is x: 123 for members and [10] "foo" for indices.

        I am surprised that they aren’t supported in C++, I wonder why that is. Perhaps they conflict with some other C++ syntax?

        1. 1

          As of C++11, initialiser lists are an object type and can be forwarded and so on. The array indexing extension doesn’t play very nicely with this because it’s special constructor syntax of a single kind of object. It also doesn’t play so nicely with type deduction.

        2. 1

          I’m pretty sure the first one is wrong.

          I think so too, but mainly as a bit of semantic hair-splitting. Technically, it’s the type that is causing the side effect, not sizeof.

          1. 1

            Damn, I was about to ask whether int b[static 12][*][*] is valid C++.

            I realize you can use std::array to express this in C++, but a lot of my work involves public C APIs around C++ implementations, so making those APIs safer and more expressive would be great.

          2. 5

            One thing I learned while working at Sourcefire VRT on the ClamAV core dev team: never write “clever code.” The reason being that clever code tends to introduce unneeded complexity, long-term maintenance burdens, and potential vulnerabilities. When we write clever code, we sabotage our future selves in addition to other developers, who may not understand the WHY behind the clever code.

            If there’s now way getting around having to write clever code, then an explicit comment describing not only what the code does, but why it was written that way should be a requirement.

            Writing clear and concise code, which would include insightful comments, relaxes all the burdens listed above. It also enables efficient development since brain cycles don’t need to be wasted understanding the complexity of that particular part of the codebase and its ramifications elsewhere.

            I would consider every single part of this article as demonstrating clever code–things to avoid. I don’t think the article intends to read that way, but that’s what I take away from it.

            1. 5

              Cleverness for its own sake can be bad, but the constructs here have valid uses.

              • The array bound designators make APIs more expressive to the reader, and enable compile- and run-time safety checks that can prevent buffer overruns.
              • Precise specification of float values, down to the bit, is important in some math/numerics code.
              • Being able to declare data structures at compile time so they’re built into the binary reduces code size and RAM usage and is important in embedded development.

              IMO if you value clear and concise code you shouldn’t be using C in the first place. The lack of safety and abstraction contribute to verbosity and fragility.

              1. 2

                Preprocessor as a functional language is heavily employed in C++ projects that define many operations (LLVM, PyTorch).

                1. 2

                  Writing clear and concise code

                  The preprocessor is your greatest friend in this regard :)

                2. 1

                  Macro calls in #include

                  Okay that was really surprising.

                  Awkward pointer declaration

                  But this is already rather infamous.

                  Stories with similar links:

                  1. Some obscure C features via calvin 3 years ago | 33 points | 3 comments