Path: csiph.com!eternal-september.org!feeder.eternal-september.org!nntp.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Constants and undefined behavior Date: Tue, 02 Jun 2026 05:06:18 -0700 Organization: A noiseless patient Spider Lines: 72 Message-ID: <86ik81cfk5.fsf_-_@linuxsc.com> References: <10v7b32$2u85v$1@dont-email.me> <10vcqjc$con8$1@dont-email.me> <10vcui4$buur$3@kst.eternal-september.org> <10vd1tu$ekvl$1@dont-email.me> <10vel6r$l1g$1@reader1.panix.com> <10vemqf$r5qe$1@dont-email.me> <10vfsmo$16jap$1@kst.eternal-september.org> <10vgqhf$1d6tp$1@dont-email.me> <10vh074$1epj1$2@dont-email.me> <10vh1eo$1ei50$2@dont-email.me> <10vh5f0$1g1ke$1@dont-email.me> <10vhgau$1iv7l$1@dont-email.me> <10vhkgl$1k67h$1@dont-email.me> <10vie7n$1r8io$1@kst.eternal-september.org> <10vm8tj$3uus7$7@dont-email.me> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Date: Tue, 02 Jun 2026 12:06:20 +0000 (UTC) Injection-Info: dont-email.me; logging-data="3072559"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/xPTDUf3oGApWlpCtn9EXKQxgKJmkcgHs="; posting-host="083f4b0f6298cfc58dbdfdd9508127b4" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:wFweKRC8S7/rMeApjs6nJX44+x4= sha1:VjIX4kpUTlQAQ6hwq39KEn9rZ+w= sha256:cRj+DjeYTYKKMMhQBQ7f9QlkYG4TZziFQh/wbw14Sy4= sha1:y/7uaFADp2o7nt8Sxb2ia+D43fY= Xref: csiph.com comp.lang.c:399615 Janis Papanagnou writes: > On 2026-06-01 00:54, Keith Thompson wrote: > >>> [...] >> >> Yes, a compiler can reduce (a + b) * 0 to just 0. But it's not >> required to do so, and (INT_MAX + 1) * 0 still has undefined >> behavior. Undefined behavior is determined by the rules of the >> abstract machine *without* any adjustments permitted by the as-if >> rule. > > This is something I really don't get in the actual C-logic... > > Using constants that can be determined at compile time is UB here, > despite the '* 0' mathematically indicating an IMO clear semantics, > but using variables is only UB possibly at runtime? [...] There's an important distinction to make here. Consider this program: #include int foo(){ int zero = (INT_MAX+1)*0; return zero; } int main(){ return 0; } This program does not transgress the bounds of undefined behavior. Even more than that, the program is strictly conforming, and must be accepted by a conforming implementation. Now let's change the program slightly: #include int foo(){ static int zero = (INT_MAX+1)*0; return zero; } int main(){ return 0; } This program does transgress the bounds of undefined behavior. The reason for the difference is that in the first program the semantics of foo() is to evaluate the expression to be stored in 'zero' only at runtime, whereas in the second program the semantics of foo() is to evaluate the expression to be stored in 'zero' before program startup (informally, "at compile time"). What matters is not whether the offending expression /might/ be evaluated "at compile time", but whether the offending expression /must/ be evaluated "at compile time". Only in the second case is undefined behavior inevitable (and thus it does not occur in the first program). Fine point: strictly speaking, I believe the C standard allows even the second program to complete translation phase 8 successfully, and for any offending behavior to occur only when we actually try to run the program. To say that another way, there is no requirement that possible nasal demons be made manifest at any point before an actual attempted execution. On the other hand, because that possibility is there lurking in the background, there is no requirement that the program be accepted, and could be rejected by a conforming compiler.