5 ms·
I haven't done C in at least a decade, so bare with me. > 1. The value of a pointer to an object whose lifetime has ended remains the same as it was when the o
by otikik 12y ago
I haven't done C in at least a decade, so bare with me.
> 1. The value of a pointer to an object whose lifetime has ended remains the same as it was when the object was alive
> 8. A read from uninitialized storage returns an unspecified value.
Isn't that already the case in C?
> 3. Shift by negative or shift-past-bitwidth produces an unspecified result.
What is the meaning of "an unspecified result" there?
> 4. Reading from an invalid pointer either traps or produces an unspecified value.
What is the meaning of "traps" here? Is it the same as later on ("math- and memory-related traps")?
- peff 12y agoNo, those are things that _usually_ happen in most C implementations. But reading from uninitialized memory provokes "undefined behavior" according to the standard. Which might mean returning an unspecified value, might mean whole chunks of code being skipped, or might mean demons flying out of your nose. The first one seems like an obvious choice of behavior, and one that some C programs doubtless depend on. But when you throw advanced optimizations into the mix, compilers may do unexpected things (e.g., assuming that a certain thing can't happen because it's not allowed by the standard, and then manipulating the code under that assumption). If you read other articles from Regehr, he describes some examples.
- angersock 12y agoThe linked flamewar on the GCC issue tracker ( https://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475#c36 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475#c36 ) is an excellent example of this: citing "undefined behavior", compiler writers will gleefully do all kinds of goofy shit and, if confronted by developers who actually program for a living, stick their fingers in their ears and go "nyah nyah nyah undefined behavior we can do whatever we want go learn c nyah nyah nyah". "Undefined behavior" is a really great trick for promoting pet optimization projects and stonewalling practical feature requests by use of language lawyering.
- DSMan195276 12y agoHow was it goofy? Personally what the compiler did made perfect sense to me. If you assume integers can't overflow, then 'b' must be larger then 'a'. Thus why would the compiler bother performing the statement 'c=(b>a)' when it's 'obvious' that it's just going to be 'c=1'? That said, looking at that page the guy who made this bug was being more then extremely annoying, the person responding to the bug was fairly civil all things considered. You're complaining about UD but it's a necessary evil in C. Integer overflow was perhaps a bad choice by the standards makers, but the fact still stands that even if GCC did the 'right' thing, there's no guarantee that clang or any other compiler will do the same thing. The code would still be broken, it just might be harder to figure that out. If you want integer overflow and wrapping then use the compiler flag for it and write non-standard code. IMO, the bigger problem is that people write their code, compile it with gcc, and then assume it's standards compliant because it 'works' with gcc.
- angersock 12y agoStack overflow question illustrating the problem: http://stackoverflow.com/questions/7682477/why-does-integer-overflow-on-x86-with-gcc-cause-an-infinite-loop http://stackoverflow.com/questions/7682477/why-does-integer-... "Principle of Least Surprise" is that an integer will wrap, because that's what the hardware does in pretty much all cases. Any clever optimizations or undefined behavior should be happening due to explicit flags--which is exactly what the guy in that bug report wanted. The thing is that, for like the last half-century, we've expected integers to overflow and wraparound..that's just how they work. Ignoring that kind of expectation is asinine.
- DSMan195276 12y agoThere is an explicit flag, he's compiling with '-O2'. As he noted, without -O2 the output is correct. gcc does exactly what you're saying it should do in this instance, so I don't see what you're unhappy about.
- angersock 12y ago
- to3m 12y agoBut undefined behaviour isn't disallowed by the standard, and these things aren't not allowed to happen! If they were, the standard wouldn't even bother to mention any of it, and certainly wouldn't bother to suggest that one option is for things to behave "during translation or program execution in some documented manner characteristic of the platform". (See the C11 draft standard, 3.4.3.2; wording is basically the same in C99 I think.) It seemed obvious to me from the moment I first heard about this stuff that undefined behaviour is there to avoid binding implementations' hands too tightly. It's a way of allowing as wide a range of implementations as practical to be standard-compliant, by not forcing the compiler to patch over every last difference between systems or provide missing functionality. But is it a way to let gcc do whatever it likes, having proven your program invalid on a technicality? Well... I'm less sure about that one. (Obligatory links: http://blog.metaobject.com/2014/04/cc-osmartass.html http://blog.metaobject.com/2014/04/cc-osmartass.html, http://robertoconcerto.blogspot.co.uk/2010/10/strict-aliasing.html http://robertoconcerto.blogspot.co.uk/2010/10/strict-aliasin...)
- comex 12y agoThe suggestions that performance improvements brought by compiler optimizations are meaningless bother me, though. First, because hardware isn't getting faster that quickly anymore: Moore's Law hasn't meant for a long time that CPUs actually double their per-thread performance every 18 months, so that "1/10th as effective" from your first link, which refers to compilers hypothetically doubling performance every 18 years, starts to get more and more attractive. Second, because while in C/C++ code the programmer can often avoid useless machine code, newer languages such as Rust and Swift tend to do more stuff implicitly (safety checks, reference counting) which could often be eliminated by a Sufficiently Smart Compiler - increasing the need for good optimizations. (I think that this somewhat mirrors C++'s early history compared to C, but I was too young then to have any personal experience.) Of course those languages also tend to have no undefined behavior, so it's a bit different... Third, because I don't think undefined behavior is as evil and hard to avoid as people think it is. I think there are a few weird points (you can cast pointers into malloced buffers to any type as long as you're consistent, but there's no way to do that for a static buffer without fully static layout), and it would be nice to have more control over things like aliasing - both to loosen rules and to tighten them (i.e. more flexible restrict-like functionality). But despite being a fun topic, it doesn't seem to come up that often in practice from what I've seen, so the performance gain is close to free. And when you're, say, fighting for 60fps in a CPU limited scenario, it's hard to turn down even a small free performance gain. Fourth, because as someone who reads assembly frequently, idiotic looking assembly bothers me aesthetically even if it often doesn't have much performance impact. I speak in particular of reloading struct fields over and over when the data is already in a register and any person looking at the code would know it would be illogical for it to change in memory since it was loaded - but the compiler isn't smart enough to prove it can't alias... Sure, in individual cases it's easy to cache it in a local variable to stop this from happening, but in the large it's hard to avoid. Strict aliasing improves the situation somewhat, which is one reason I like it.
- readerrrr 12y ago8. Currently the behaviour is undefined not unspecified. Undefined behaviour means anything can happen from that point on( bad ), while unspecified just gives you an unspecified value. 3. Again unspecified vs. undefined. 1. From C11 Standard: The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime. When you use that pointer you get undefined b.
- mark-r 12y agoUndefined behavior isn't limited to from that point on, it can have terribly surprising consequences. A great example at: http://blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx http://blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537...
- readerrrr 12y agoYou misinterpreted my statement. From that point on means when you code it. I have read that article before; the usage of the word time travel is nonsense. Author could just as well say the code preceding the ub can get mangled and avoid confusion.
- BrandonM 12y ago> You misinterpreted my statement. From that point on means when you code it. That's a very aggressive way to phrase, "Sorry, I stated that unclearly."
- DSMan195276 12y agoFor 8 and 3, I believe you're right (That's what I was thinking too - undefined-behavior), but I think they're making some type of distinction between UD and a undefined value/result. In C right now, there isn't any guarantee your code will actually keep going since it's UD, with an 'undefined value' I presume it means you still get some type of number and your code keeps going with that bad number (Which may or may not have bad consequences. I'm not really sure this is better then just UD, but most implementations just give you whatever random value was there anyway so it wouldn't really change much besides guaranteeing your code will run with a bad value instead of the compiler blowing up on the UD). As for 4, I was also curious on that. I think they're just trying to specify something more specific then UD, like a SIGSEGV (or whatever signal thing Windows does). Compilers basically do that already though, and any architecture that doesn't do some type of trap on an invalid pointer probably doesn't have the infrastructure to do so. That said, the biggest issue is not what the trap is, but how the trap is caught or handled. It's not really that helpful of a standard if you know it's going to "trap", but you have no standard way of handling such a trap.
- thedufer 12y ago"Undefined behavior" affects the entire output of the compilation - it allows the compiler to change anything, anywhere (dropping overflow checks on the assumption that overflow can't happen is perfectly legal, for example). An "unspecified value"/"undefined value" seems to mean that the associated operation can return anything it wants, but can't affect adjacent code.
- rcthompson 12y agoIf I'm understanding correctly, I think the difference between "undefined behavior" and "returns an unspecified value" is consitency. If you had something like: int i = [UNDEFINED BEHAVIOR]; if (i > 0) COND1; if (i > 0) COND2; then most people would expect that either COND1 and COND2 both execute, or they both don't execute. But I believe that a compiler is theoretically free to produce code that executes one but not the other since the value of i is undefined. In other words, the compiler doesn't have to act as though i has one specific value after that assignment. It can assume any value it likes independently each time i is used. It can assume that i is positive at the first check and then assume it's negative the next time, even though it might be provably true that no code between those two checks can possibly change the value of i. The change to "unspecified value" would mean that the compiler can give i whatever value it wants to, but it has to be a single defined value, and subsequent code must not behave as though i had multiple changing values. But I'm not really a C programmer, so feel free to correct me if I'm interpreting this wrong.
- readerrrr 12y agoUndefined behaviour means that anything can happen, the program might exit,start displaying random numbers or format your hard drive.
- mikeash 12y agoThe classic example of undefined behavior is "nasal demons". Which is to say, that when you hit undefined behavior, the compiler would be completely within its rights to make demons fly out of your nose. "Undefined behavior" means the compiler can do anything. In your example, the compiler could execute one statement but not the other. Or it could execute both of them fifteen times. Or it could reformat your hard drive, or start a war. All would be legal according to the spec. (Feasibility of implementing such things is, of course, another question.) The more mundane consequences are more likely to be what you actually see, but the point is that you can't really reason about it in the abstract. You have to know exactly how your particular compiler handles it, and it can go well beyond just executing your code in a funny way. For one real-world that approaches "nasal demons", early versions of gcc would start a game of nethack if they encountered a #pragma statement in your program.
- rayiner 12y agoRead the semantics of the undefined value in LLVM: http://llvm.org/docs/LangRef.html#undefined-values http://llvm.org/docs/LangRef.html#undefined-values. > the short answer is that an ‘undef‘ “variable” can arbitrarily change its value over its “live range”. This is true because the variable doesn’t actually have a live range. Instead, the value is logically read from arbitrary registers that happen to be around when needed, so the value is not necessarily consistent over time. An undefined value is both unspecified but also need not be the same unspecified value over multiple uses. This can result in surprising behavior when you're reading code, where you tend to assume that even if the value of a variable is unknown, different uses of it will be consistent with respect to each other.