Path: csiph.com!eternal-september.org!feeder.eternal-september.org!mx02.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: the language (and the role of Undefined Behavior therein) Date: Thu, 20 Aug 2015 12:32:56 -0700 Organization: None to speak of Lines: 103 Message-ID: References: <5yhkw.397413$il4.43434@fx06.am4> <5492c298.300765@news.xs4all.nl> <90183436-b839-4ee9-a3ed-a9eab341e847@googlegroups.com> <_MQAx.4834$IB7.2230@fx07.iad> <896d7bb0-72f3-4905-847e-8f6b6246cfc0@googlegroups.com> <55D4E2C9.8030109@verizon.net> <430b14cb-c335-4e54-94c5-d1076a2142fe@googlegroups.com> <55D51DE6.6070606@verizon.net> <75e8b3e2-4171-40bf-9e2d-c3c132272dec@googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: mx02.eternal-september.org; posting-host="945944de09706c9b4e29b53c9d2efdc2"; logging-data="30334"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+VP25Z+HcA0GMb4g3mLOvo" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) Cancel-Lock: sha1:/QXAaoRy1iiILTe5BgZxAuh5ero= sha1:XDI+Zk7lGWE95oKv05d7COin9zo= Xref: csiph.com comp.lang.c:67900 supercat@casperkitty.com writes: > On Wednesday, August 19, 2015 at 7:23:08 PM UTC-5, James Kuyper wrote: >> The laws of time and causality don't say that foo==32 has to cause bar() >> to be executed - it's the C standard which says that; and it only says >> it in contexts where the behavior is defined. When the behavior is >> undefined, the standard revokes that requirement, so a compiler can >> generate code (which behaves strictly according to the laws of time and >> causality) to produce a result quite different from that one. > > I am well aware that C11 says that the fact that a program is "going to" > perform Undefined Behavior makes execution undefined even before it is > invoked. C99 and C89/C90 say the same thing; there's been no substantial change in the meaning of "undefined behavior". Or am I missing something? > I don't think debating the terminology is helpful, though there is > a point I'm not clear on: given > > #include > int main(void) > { > int ch; > printf("DO NOT press any key!\n"); > ch = getchar(); > if (ch >= 32) return 1 << ch; else return 1 >> (ch - 32); > } > > would execution be well-defined if the user doesn't hits a key before > execution is halted by some means (SIGINT, "kill -9", removal of > power, etc.)? If getchar() returns, there will be no defined > execution path (and a compiler could probably recognize that pretty > easily), and the language of C11 suggests that UB can time-travel > before I/O, but there's no way the abstract machine could perform an > invalid left-shift if getch() doesn't return a value greater than 31, > nor an invalid right-shift if getch() doesn't return a value less than > 32. Could a compiler move the UB before the getch() despite the data > dependency? The compiler does not "move the UB". It generates code consistent with the possible range of *defined* behaviors. If the program has undefined behavior, that range is unbounded, and the generated code can do literally anything (any bounds are imposed by things other than the C standard, like the capabilities of the underlying hardware or the laws of physics). The behavior of the above code is undefined in all circumstances. The use of an input function muddies the waters a little bit, but IMHO not significantly. Any program can be terminated by external means at any time; doing so during an input operation is not special. A compiler can re-order operations as long as the resulting behavior is still conforming. If that re-ordering causes some seemingly later operations to occur while earlier operations do not because someone pulled the power plug, that's not a violation of the standard. A compiler could, for example, recognize that the program's behavior is always undefined, and reject the program with an error message to that effect. Or it could generate code that attempts to make demons fly out of hour nose before the "DO NOT press any key!" message is printed. >> That would be odd, because currently, the amount of ugly and >> inefficient code required to avoid undefined behavior is relatively >> minor. > > The amount of ugly and inefficient code employed to avoid UB seems > minor because a lot of code *doesn't* avoid it. Instead, a lot of > code was written on the assumption that if every compiler released in > the last 10+ years for a mainstream platform would behave acceptably, > future compilers would continue to do so. What exactly happened 10 years ago? [...] > Handling overflow in such fashion as to meet requirements cannot be > done quickly nor easily on *all* platforms, but any non-obscure > hardware platform built in the last 20 years would have no problem > whatsoever performing some calculation that would meet requirements. > Until recently, one could expect that any non-obscure platform, given > the original source code, would perform such a calculation (likely the > most efficient one) unless "trap on overflow" was explicitly > requested. > > Even today, programmers can and do avoid the burden by using -wrapv, > but doing so often imposes constraints on the compiler which are much > tighter than what the programmer actually needs, and also creates the > danger that the code which relies upon the -wrapv option might > accidentally get compiled without it. C states that arithmetic overflow has undefined behavior, but provides no coherent mechanisms to reliably detect or avoid it. C programmers tend to deal with that problem by ignoring it. We tend to be lucky; most arithmetic operations *don't* overflow, but we rarely attempt to guarantee that. This is IMHO a flaw in the language. [...] -- Keith Thompson (The_Other_Keith) kst-u@mib.org Working, but not speaking, for JetHead Development, Inc. "We must do something. This is something. Therefore, we must do this." -- Antony Jay and Jonathan Lynn, "Yes Minister"