Live data from Hacker News

Mildly interesting quirks of C

gist.github.com

51–60 of 91 posts

Re: Mildly interesting quirks of C

#51
post #15

Whenever the subject of C/C++ quirks is brought up, I always like to point out the Deep C/C++ presentation: http://www.pvv.org/~oma/DeepC_slides_oct2011.pdf Source: https://freecomputerbooks.com/Deep-C-and-Cpp.html#downloadLi... Previous discussion: https://news.ycombinator.com/item?id=3093323 It could be considered a bit dated at this point (It's before C++11) but I find it still both entertaining and educating.

No post body was provided.

Re: Mildly interesting quirks of C

#52

Can someone explain how "A constant-expression macro that tells you if an expression is an integer constant" works ?

If `x` is a constant, `(x) * 0l` is a zero constant, so `(void*)((x) * 0l)` is a null pointer. When a null void pointer is one branch of a ternary conditional, the expression takes the (pointer) type of the other branch.

If `x` is not a constant, `(void*)((x) * 0l)` is a void pointer to address 0 (which may not even be a null pointer at runtime, since null may have a runtime address distinct from zero!). The ternary conditional then unifies the types of the branches, resulting in `void*`.

Re: Mildly interesting quirks of C

#54
post #6

Reminds me a bit of "Who Says C is Simple?" written by the people who wrote a C parser & analyser in OCaml (CIL): https://cil-project.github.io/cil/doc/html/cil/cil016.html Also: https://cil-project.github.io/cil/doc/html/cil/cil012.html

> "Who Says C is Simple?" People who don't know what "simple" means and confuse it with "easy". https://www.entropywins.wtf/blog/2017/01/02/simple-is-not-ea... https://www.infoq.com/presentations/Simple-Made-Easy/ "Easy" things almost always lead to astonishing complexity. Also it's easy to see just how complex C is: Have a look at a formal description of it! (And compare to a truly simple language like e.g. LISP). h…

+1 for simple is not easy, yet with enough thinking and ingenious ideas, it is achievable. Thank for the links.

"simplicity is the ultimate sophistication." -- Leonardo da Vinci

Re: Mildly interesting quirks of C

#55
I consider the array pointer stuff a bit of a foot-gun in C. I've seen too many examples of people mixing up uint8_t[][] and uint8_t**.

The "compound literals are lvalues" pattern I've seen many times for inline initializing a struct that's only going to be around as a parameter to a single function call.

Re: Mildly interesting quirks of C

#56
post #20

Earlier quoted context omitted.

I really wish both would be valid in C11. Or rather I wish I had "systems-C" where all the undefined behaviour added for high performance computing was filed off and defined as "whatever the platform does".

> all the undefined behaviour added for high performance computing UBs were added for cross-incompatibilities, where operations were too "core" (and / or untestable) for IBs to be acceptable. The reason was not performance (aside from not imposing a runtime check where that would have been possible) but portability: > 3.4.3 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of er…

The standard also has implementation-defined behavior, doesn't it?

Re: Mildly interesting quirks of C

#57

> UB is impossible What? UB is clearly undesirable , but assuming it is impossible and deducing other outcomes must be meant are clearly wrong assumptions by the compiler writer. More sensible compilers (including older version of clang) do the right thing (TM) here and yield a compiler error. There were earlier attempts at do-what-i-mean programming languages. They are rightfully buried in history.

> UB is clearly undesirable, but assuming it is impossible and deducing other outcomes must be meant are clearly wrong assumptions by the compiler writer.

Compilers can and absolutely do assume that UB is impossible in this code (no integer overflow) and deduce other outcomes must be meant (the loop operates on contiguous memory):

  void foo(char* arr, int32_t end)
  {
    for (int32_t i = 0; i != end; ++i)
      arr[i] = 0;
  }
(Based on code from the gist comments.)

Re: Mildly interesting quirks of C

#58
post #16

My favorite C "quirk": If you have an array and you want to access an item of it, you can swap the variable and the index number (put the variable name inside brackets and the number outside): a[5] is the same as: 5[a] why? a[5] is actually sugar for *(a + 5), so by commutative property, you can also do *(5 + a) to access the same memory position :-)

It's one of the B leftovers in C - In B, the only type is "machine word", and words are interpreted as ints or pointers depending on the operators used. Thus, distinguishing between a[i] and i[a] is impossible, so both were valid.

Array-to-pointer decay is another manifestation of this.

Re: Mildly interesting quirks of C

#59

Regarding 12, alignment of bitfields, how I believe it works is that when the bitfield of type long is laid out, then the structure so far is considered to be a vector of storage cells whose size and alignment are those of long: struct foo { char a; long b: 16; char c; }; So, a has been laid into the structure, so the current offset is 1 byte. This is considered to be occupying a portion of an existing long type bitf…

The cell size is not necessarily the same as "long" - it can be whatever the compiler wants, so long as alignment of non-bitfield fields is appropriate. It doesn't even have to be the same for every bitfield.

Re: Mildly interesting quirks of C

#60
post #20

Here are two of my favorite obscure quirks of C: struct X { char x[8]; }; struct X awoo(void); printf("%s\n", awoo().x); The above is UB in = C11. [0] struct X { char b[8]; } foo(); int *b = foo().b; printf("%s\n", b); The above is UB in >= C11 and valid in [0] https://wiki.sei.cmu.edu/confluence/plugins/servlet/mobile?c... [1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1285.htm

I really wish both would be valid in C11. Or rather I wish I had "systems-C" where all the undefined behaviour added for high performance computing was filed off and defined as "whatever the platform does".

Any given implementation is free to define any particular instance of UB. "Whatever the platform does" is still UB for portable code though, since the set of possible platforms and their behavior is unbounded.
Post reply on HN