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.