Here's a list of mildly interesting things about the C language that I learned over time.
- Combined type and variable/field declaration, inside a struct scope: https://godbolt.org/g/Rh94Go
- Compound literals are lvalues: https://godbolt.org/g/Zup5ZB
- Switch cases anywhere: https://godbolt.org/g/fSeL18 (also see: Duff's Device)
- Flexible array members: https://godbolt.org/g/HCjfzX
- {0} as a universal initializer: https://godbolt.org/g/MPKkXv
- Function typedefs: https://godbolt.org/g/5ctrLv
- Array pointers: https://godbolt.org/g/N85dvv
- Modifiers to array sizes in parameter definitions: https://godbolt.org/z/SKS38s
- Flat initializer lists: https://godbolt.org/g/RmwnoG
- What’s an lvalue, anyway: https://godbolt.org/g/5echfM
- Void globals: https://godbolt.org/z/C52Wn2
- Alignment implications of bitfields: https://godbolt.org/z/KmB4CB
Special mentions:
- The power of UB: https://godbolt.org/g/H6mBFT. This happens because:
- LLVM sees that
side_effectshas only two possible values: NULL (the initial value) orthis_is_not_directly_called_by_main(ifbaris called) - LLVM sees that
side_effectsis called, and it is UB to call a null pointer - UB is impossible, so LLVM assumes that
barwill have executed by the timemainruns rather than face the consequences - Under this assumption,
side_effectsis alwaysthis_is_not_directly_called_by_main.
- LLVM sees that
- A macro that tells you if an expression is an integer constant, if you can't use
__builtin_constant_p: https://godbolt.org/g/a41gmx (from Martin Uecker, on the Linux kernel ML) - You can make some pretty weird stuff in C, but for a real disaster, you need C++. Labels inside expression statements in really weird places: https://godbolt.org/g/k9wDRf.
(I have a bunch of mildly interesting in C++ too, but so does literally everyone who’s used the language for more than an hour, so it’s not as interesting.)
Please avoid posting C program code that hasn't gone through thorough review (and had all warnings, including
-Wall -Wextra -Wpedantic, fixed, as well as being compiled according to a C standard-std=c11). Even the pedantic warnings are there for good reasons. Many really speak for themselves.Cherish the warnings that you are actually getting. The more subtle cases of UB (like a null pointer not being required to be a pattern of 0, which is why memset(a, 0, sz) is not strictly correct/technically UB) you will not hear about, and the compiler isn't required to warn you about other cases either.
2:
Empty initializer braces may be part of C++, but they're not allowed in C according to the standard.
The more interesting use cases for these compound literals is that you can pass them into functions, either their value or a pointer to them, without having to put them somewhere nearby in automatic storage, which is admittedly not that useful or unique, and more importantly, allowing you to reset a struct to 0, while correctly setting pointers they contain to null, which
memsetwill not strictly do.4:
5:
This is so wrong that gcc gives you a warning and an error for the same thing. Compiler extensions are strictly not part of the C language.
9:
While not strictly required, a warning is still printed, even giving you the correct braces, because it's so easy to introduce bugs otherwise.
12:
Nearly everything about bitfields is horrifyingly implementation-dependent, so results will vary from compiler to compiler, as such your paste is completely pointless and devoid of useful information.
To force a bitfield to be aligned "as you would expect", which is "overlaid over the basic integer type", one would use:
which is then laid out the same way as this struct (which is 24 bytes in size, with a 8 byte size 8 byte alignment long):
(this is obviously still very implementation-dependent)
"special mentions 1":
This is not "interesting UB", this is just UB which is to be avoided at all times. Never ever write code this way.