Live data from Hacker News

A glimpse of undefined behavior in C

blog.chris-cole.net

31–40 of 67 posts

Re: A glimpse of undefined behavior in C

#31
post #7

Knowing how exactly sequence points work might be an advanced topic, but I am not sure I'd hire a junior C/C++ programmer if he has not heard of sequence points. If somebody has, then he clearly won't do something like this code. I am not 100% sure in how they work either (haven't programmed C for 4 years), so I just avoid these kind of unnecessary complications in the code, if not for myself, then for the guy who co…

I am not 100% sure in how they work either (haven't programmed C for 4 years), so I just avoid these kind of unnecessary complications in the code Hear, hear. I am considered the "language lawyer" of my embedded group and often get asked questions about C minutiæ. It isn't uncommon that my answer is "I don't know how that works, because I would never write something that requires an answer to that". Modern compilers…

Const-ness is really different. It's a concept I really miss from other languages as it _adds_ more semantic information to the code. It does not obscure, it clarifies. It's a way to give orders instead of giving recommendations.

Re: A glimpse of undefined behavior in C

#32

Earlier quoted context omitted.

Ok, so do any other compilers handle it differently?

Practically every new version of gcc adds a new optimization that recognizes some new form of undefined behavior and then rewrites your function to do whatever it wants. Classic example: signed integer overflow. It worked for decades. Then one day it didn't. If you want to know about this particular example: https://news.ycombinator.com/item?id=6824514 (not personally confirmed)

That isn't really an accurate description of the issues: it's not the case that it was "working" and then "broken" by GCC maintainers. It's always been unsupported and not worked in specific situations, but for 99% of code it appeared to GCC users that it was supported. The signed integer overflow behaviour was never guaranteed by old versions of GCC, and code exploiting it would not always be compiled with the "expected" behaviour. It's just as the GCC optimizer has improved there are more circumstances when it does optimisations that hinge on the assumption.

Compiler users generally want something that "just works" and doesn't do anything unexpected, but in the case of a low-level language like C, doing away with undefined behavior essentially would mean pessimistically avoiding many optimisations on the 99%+ of straightforward, reasonable code out there in favor of not doing anything surprising on the remaining fraction of dubious code that depends on certain things happening in scenarios where behavior is undefined according to the C standard. There are languages that make that choice, but C isn't one of them.

Re: A glimpse of undefined behavior in C

#34

The way I learned that in C a long time ago was "post increment" was "post statement increment".

There was a popular myth way back in the 90's that preincrement was always faster than postincrement because the former could generate a temporary. As if this were the case:

    /* x++ */
    inline int postincrement(int *x) {
      int temp = *x;
      *x = *x + 1;
      return temp;
    }

    /* ++x */
    inline int preincrement(int *x) {
      *x = *x + 1;
      return *x;
    }

But in typical usage, where the expression value is not used, it doesn't make any difference:

    for (int i = 0; i 
If one looks at the assembly, it's clearly equivalent to:

    for (int i = 0; i 
Incidently, the latter seems more readable.

Re: A glimpse of undefined behavior in C

#35
post #27
post #25

Why? What reason could there possibly be that such well known, gaping holes in the standard have persisted for all these years and multiple standard revisions? C90, C99, C11 and now C14... If performance or backward compatibility is a concern, surely an optional macro like __STDC_STRICT__ could be suggested in the standard with clear expectations that could be relied on.

That's a reasonable question. It's a shame this comment gets downvoted instead of the various people falling all over themselves to show off how smart they are by posting incorrect guesses about how undefined behavior doesn't matter. Here's a good article that addresses the question "Why have undefined behavior?" http://blog.regehr.org/archives/213 This one linking to the above is also worth reading: http://blog.llvm…

People seem to be forgetting that an implementation includes an execution environment as well as just a compiler, and that a perfectly valid (and, to my mind, very much recommended) option for undefined behaviour is to behave in some documented manner characteristic of the platform. I don't see this "no obligation" stuff as a great idea, not when it results in so much surprising behaviour - and especially not for simple situations where there's something obvious that the compiler could do, that would surprise absolutely nobody familiar with the system in question.

I always imagined undefined behaviour was there to avoid tying implementations' hands, by having the standard not mandate things that vary in practice. But it seems that people are assuming it's there to give compiler writers carte blanche to do whatever they like, and then point at the standard as justification. Given how much stuff could potentially be added to C to improve it, I don't know why people are spending all this time trying to figure out all the ways in which the letter of the law allows them to confuse the programmer.

See also somebody else's rant about strict aliasing: http://robertoconcerto.blogspot.co.uk/2010/10/strict-aliasin...

Re: A glimpse of undefined behavior in C

#36
post #33

Isn't this actually called "implementation-defined" behaviour in the std rather than "undefined" behaviour? I generally get a segfault for undefined behaviors

I believe this is undefined because the standard doesn't say what a "sequence point" is and not "a sequence point is implementation defined". I'm going from second-hand knowledge and it's been years since I looked at C (and never past a high school level of understanding).

Re: A glimpse of undefined behavior in C

#37
post #25

Why? What reason could there possibly be that such well known, gaping holes in the standard have persisted for all these years and multiple standard revisions? C90, C99, C11 and now C14... If performance or backward compatibility is a concern, surely an optional macro like __STDC_STRICT__ could be suggested in the standard with clear expectations that could be relied on.

Hopefully, because most in-practice use of C avoids undefined (or complexly defined) behavior.

Re: A glimpse of undefined behavior in C

#39
post #34

The way I learned that in C a long time ago was "post increment" was "post statement increment".

There was a popular myth way back in the 90's that preincrement was always faster than postincrement because the former could generate a temporary. As if this were the case: /* x++ */ inline int postincrement(int *x) { int temp = *x; *x = *x + 1; return temp; } /* ++x */ inline int preincrement(int *x) { *x = *x + 1; return *x; } But in typical usage, where the expression value is not used, it doesn't make any differ…

I always thought this "myth" was confined to C++ code (iterators) where there may in fact be code that does something analogous to what your "postincrement()" does.

Compilers are sometimes smart enough to remove the unnecessary operation in C++ (e.g. switch to ++x themselves), but I always use ++i for the same reason people simplify their usage of C in between sequence points --- it's an easy transformation, and why tempt fate?

Re: A glimpse of undefined behavior in C

#40
post #19

The way I learned that in C a long time ago was "post increment" was "post statement increment".

it's a little weirder though. int main(int argc, char argv) { int a = 0; printf("%i %i %i\n", a, a++, a++); } will give "0 0 1" (gcc 4.2.1), the increment "shouldn't" happen until after the ; if you're going with post statement. I think your rule would expect "0 0 0" with a being 2 after the printf. BUT! you get a warning, so that's nice.

The example given in the article shows all the code, but your example involves a function so the code in question is hidden. The behaviour partially depends on how the function is written.

"0 0 1" is what I would expect from your code, and identifier a should end up as 2.

Post reply on HN