There was some recent longish thread with discussions addressing
Undefined Behavior. I just stumbled across a paper from Russ Cox
on that topic with a provoking title.[*] I don't recall whether
that had already been referenced anywhere in that thread; if not
it might be of interest to some. A couple arguments and examples
look familiar already, but I think it may be valuable anyway.
Janis
[*] https://research.swtch.com/ub
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
There was some recent longish thread with discussions addressing
Undefined Behavior. I just stumbled across a paper from Russ Cox
on that topic with a provoking title.[*] I don't recall whether
that had already been referenced anywhere in that thread; if not
it might be of interest to some. A couple arguments and examples
look familiar already, but I think it may be valuable anyway.
Janis
[*] https://research.swtch.com/ub
For those who don't follow the link, the full title of the paper is
"C and C++ Prioritize Performance over Correctness". (Your
paraphrase could be interpreted as advice, which I'm sure is not
how you intended it.)
```
term% cat eraseall.c
#include <stdio.h>
#include <stdlib.h>
typedef void (*Function)(void);
static Function Do;
static void
baz(void)
{
printf("baz invoked.\n");
}
static void
bar(void)
{
baz();
}
void
foo(void)
{
Do = bar;
}
int
main(void)
{
Do();
return 0;
}
term% clang -O1 -Wall -Wextra -pedantic -std=c23 -o eraseall eraseall.c
term% ./eraseall
baz invoked.
term%
```
Something similar came up a month or so ago; recall my "what.c"
program:
```
term% cat what.c
#include <stdio.h>
int main(void) { for (unsigned int k = 0; k != 1; k += 2){} return 0; }
void hello(void) { printf("Hello, World!\n"); }
term% clang -O1 -Wall -Werror -pedantic -pedantic-errors -std=c23 -o what what.c
term% ./what
Hello, World!
term%
```
It's notable that the quoted text for the ANSI C standard also
says that an implementation is free to abort translation if it
encounters UB, which I recall was something that was a point of
concention in the earlier discussion. Kuyper posted a link to
a defect report that suggested that the compiler had to
translate the program if it could prove that (in that case,
manifest) UB was never triggered; however, that was from 1994,
and not normative anyhow.
The committee has had ample time to
tune up the writing about this in the standard, and has chosen
not to: Cox's post is on-point; the current standard is deeply
flawed.
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
There was some recent longish thread with discussions addressing
Undefined Behavior. I just stumbled across a paper from Russ Cox
on that topic with a provoking title. [...]
For those who don't follow the link, the full title of the paper is
"C and C++ Prioritize Performance over Correctness". (Your
paraphrase could be interpreted as advice, which I'm sure is not
how you intended it.)
On 2026-07-22 02:26, Keith Thompson wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
There was some recent longish thread with discussions addressing
Undefined Behavior. I just stumbled across a paper from Russ Cox
on that topic with a provoking title. [...]
For those who don't follow the link, the full title of the paper is
"C and C++ Prioritize Performance over Correctness".ÿ (Your
paraphrase could be interpreted as advice, which I'm sure is not
how you intended it.)
It was actually a (deliberate) "marketing trick" to provoke interest
by reducing the title to an (IMO) yet more aggravating formulation.
Yes, it was not my intention to suggest performance over correctness.
(Would anyone?)
[...]
```
term% cat eraseall.c
#include <stdio.h>
#include <stdlib.h>
typedef void (*Function)(void);
static Function Do;
static void
baz(void)
{
printf("baz invoked.\n");
}
static void
bar(void)
{
baz();
}
void
foo(void)
{
Do = bar;
}
int
main(void)
{
Do();
return 0;
}
term% clang -O1 -Wall -Wextra -pedantic -std=c23 -o eraseall eraseall.c
term% ./eraseall
baz invoked.
term%
```
In that program, Do is a function pointer that's initialized to a null >pointer value (because it's static), so the call `Do()` has undefined >behavior.
Something similar came up a month or so ago; recall my "what.c"
program:
```
term% cat what.c
#include <stdio.h>
int main(void) { for (unsigned int k = 0; k != 1; k += 2){} return 0; }
void hello(void) { printf("Hello, World!\n"); }
term% clang -O1 -Wall -Werror -pedantic -pedantic-errors -std=c23 -o what what.c
term% ./what
Hello, World!
term%
```
That's an example of something more specific. N3220 6.8.6.1p4
gives explicit permission for an implementation to "assume" that
certain iteration statements will terminate. (I've said before
that I dislike this rule for several reasons.)
It's notable that the quoted text for the ANSI C standard also
says that an implementation is free to abort translation if it
encounters UB, which I recall was something that was a point of
concention in the earlier discussion. Kuyper posted a link to
a defect report that suggested that the compiler had to
translate the program if it could prove that (in that case,
manifest) UB was never triggered; however, that was from 1994,
and not normative anyhow.
C89 and all later editions of the C standard say (in a non-normative
note) that undefined behavior can include "terminating a
translation or execution (with the issuance of a diagnostic
message)". But almost all undefined behavior occurs at run time.
That could have been worded more clearly, but my interpretation
is that an implementation that detects that a source construct
has undefined behavior can terminate translation only if it can
prove that it's always executed. `if (0) 1/0;` does not permit
terminating translation.
The committee has had ample time to
tune up the writing about this in the standard, and has chosen
not to: Cox's post is on-point; the current standard is deeply
flawed.
How would you suggest fixing it?
#include <stdio.h>
#include <stdlib.h>
typedef void (*Function)(void);
static Function Do;
static void
baz(void)
{
printf("baz invoked.\n");
}
static void
bar(void)
{
baz();
}
void
foo(void)
{
Do = bar;
}
int
main(void)
{
Do();
return 0;
}
On 22/07/2026 07:33, Janis Papanagnou wrote:
On 2026-07-22 02:26, Keith Thompson wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
There was some recent longish thread with discussions addressing
Undefined Behavior. I just stumbled across a paper from Russ Cox
on that topic with a provoking title. [...]
For those who don't follow the link, the full title of the paper is
"C and C++ Prioritize Performance over Correctness".ÿ (Your
paraphrase could be interpreted as advice, which I'm sure is not
how you intended it.)
It was actually a (deliberate) "marketing trick" to provoke interest
by reducing the title to an (IMO) yet more aggravating formulation.
Yes, it was not my intention to suggest performance over correctness.
(Would anyone?)
Unfortunately, the answer to your final question is yes - and that is
one of the reasons UB can be a problem.ÿ If by "correctness" you mean
"code does what it should", people do sometimes release code that they
know has bugs, but they know that fixing them would slow down the code - perhaps it is still good enough for their purposes at the time.
More relevant to this discussion, if by "correct" code you mean code
that has fully defined or appropriate implementation-defined behaviour
(most code does not need to be fully portable) according to the C
standards and/or platform or compiler extended semantics, then it is absolutely the case that programmers regularly prioritise performance
over correctness by relying on UB.ÿ The result is code that works efficiently and as intended at the time, using tools tested by the developer.ÿ Then the UB bites the next person down the road that uses
the same code in good faith, but with a different compiler or different options.
If the reliance on the UB was unintentional, it's a bug - programmers
are humans, they don't always know everything, and people make mistakes.
ÿBut some programmers do so /knowing/ that the code is UB.ÿ Sometimes
it is even stated in comments.ÿ The most common cases, IME, are reliance
on two's complement wrapping, and messing with pointer casts to access
data as different types, possibly with incorrect alignment.
As for the linked paper, I am not particularly impressed by its arguments.
I think it is fair enough to have different opinions about what should
or should not be UB in language standards - and it is inarguably the
case that some things that are UB could have defined behaviour at quite minor performance costs.ÿ (Note, however, that studies looking at the performance cost of disabling certain optimisations - such as "-fno- strict-aliasing" or "-fwrapv" - almost invariably use only x86 targets.
x86 processors are designed to get the best from poorly optimised code, because that's what is often run on them, while most other processors
except compilers to do more of the work.ÿ Compiler optimisations often
have a much bigger impact on non-x86 targets and on processors with
smaller relative memory latencies.)
Fundamentally, the idea of "UB" is that it describes code that does not
make sense, where there is no correct answer or meaningful specification
of behaviour.ÿ What should it mean to dereference a pointer that does
not point to a valid object of the type you are using?ÿ What should it
mean to store a large result in a target type that is too small?ÿ Code
that does this kind of thing is buggy - it cannot give correct answers. Nothing the compiler can do could make the code correct.
The compiler's logic then goes like this:
1. UB is incorrect code.
2. C is a "trust the programmer" language.
3. The compiler assumes the programmer has written correct code.
4. Therefore, the programmer has not written code with UB.
5. Therefore, UB cannot happen.
6. Therefore the compiler can optimise on that assumption, such as by skipping some code generation.
The only question is, is it a good idea for the compiler to be allowed
to make the program "more wrong" ?ÿ I don't think it matters as much as people often think.ÿ It's easy to find or create examples where compiler optimisations on UB can amplify errors.ÿ Equally, however, you can find
or create examples where assuming some kind of na‹ve "obvious" implementation of the UB code will also lead to bad results.ÿ Programs adding two positive integers are going to expect a positive result - a wrapped negative result is going to lead to tears.ÿ You need the same
kind of care in development and testing no matter how the compiler
treats its UB - and if you have done that job, these kinds of compiler optimisations do not significantly (IMHO) affect the risk of later
problems.
The killer point, however, is when you have received some code that you
can justifiably assume is correct and well tested, but contains reliance
on how certain types of UB happen to be implemented.ÿ It is not really
any different than code that makes other undocumented assumptions, such
as implementation-dependent details, reliance on certain files or OS features, timing requirements, etc.ÿ And compiling with "-fwrapv -fno- strict-aliasing" (if you are using a compiler that supports such
features) will mitigate the most common cases of reliance on UB.
As for the paper itself, I have a few points.
It follows the time-worn path of complaining that "modern" compilers
have distorted the "original" idea of UB and abuse it for optimisation
in ways that are dangerous to the uninitiated.ÿ First, optimisation
using UB is not new - I first used a compiler that assumed integer
overflow does not wrap over thirty years ago.ÿ Second, compilers already default to "-O0" - if you know so little about your tools that you can't enable warnings, you won't have optimisation either.ÿ Third,
improvements in compiler optimisation have gone hand-in-hand with improvements in compiler static error checking.ÿ Using "-Wall" along
with optimisation will eliminate the solid majority of the risks and
worries of the author.ÿ Fourth, the development of "sanitizers" not only allows far more UB to be found during testing, but it allows far more
bugs to be found than would be possible if the mistakes were /not/ UB - "-fsanitize=signed-integer-overflow" catches overflow bugs precisely
because it is UB in C, whereas if it were defines as wrapping then the mistake in the programmer's code would be correct as far as the language
is concerned.
Compilers are, of course, not perfect.ÿ But they are usually pretty
good.ÿ Most of the paper could be scraped and replaced with the advice
of always using "-Wall", possibly with "-Wextra" and/or fine-tuning of
other warning flags, regardless of optimisation.ÿ And make use of
sanitizers while testing.ÿ And always test with solid optimisations
enabled (like "-O2"), though lower optimisations can be handy for fault- finding and debugging some aspects of code.
It repeats the myths about signed integer overflow being UB because some computers have different representations of signed integers - C uses implementation-defined behaviour to handle implementation differences,
and only resorts to UB when there is no sensible definition of "correct" behaviour, or where implementation-defined behaviour could have
significant overhead costs.
Both C++ (from C++20, IIRC) and C (from C23) allow only two's complement signed integer representations.ÿ Both committees made it clear that
signed arithmetic overflow is still UB, because it does not lead to a sensible answer and allows optimisations and additional error checking.
It is not hard to get wrapping behaviour if you want it (using
conversions to and from unsigned types), and C23 introduced standardised checked arithmetic functions.
As for infinite loops, loops with a constant controlling expression
(like "for (;;)" ) are exempt from the "assumed to terminate" clause in
C.ÿ I don't know if the example given was a bug in Clang or if it used
C++ and C++ has different rules here.
And in the next example, merging two loops into one, the merge won't introduce new bugs or race conditions - if another thread is using
"count2", then there needs to be some kind of coordination (like
atomics), and then the loops could not be merged anyway.ÿ I am not
convinced that allowing the compiler to assume termination of certain
types of loop is a particularly useful feature - it seems to me to be an added complication with very few potential benefits.ÿ But I can't see it being a problem for sensible working code either.
Don't dereference null pointers.ÿ It's a bad idea.
Read the specifications of the functions you want to use.ÿ std::sort requires a comparison function with a strict weak ordering - give it something else, and it's a user bug.ÿ Perhaps std::sort could have been specified in a nicer way, but that is hardly a compiler problem.
On 7/22/2026 3:41 AM, David Brown wrote:
On 22/07/2026 07:33, Janis Papanagnou wrote:
On 2026-07-22 02:26, Keith Thompson wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
There was some recent longish thread with discussions addressing
Undefined Behavior. I just stumbled across a paper from Russ Cox
on that topic with a provoking title. [...]
For those who don't follow the link, the full title of the paper is
"C and C++ Prioritize Performance over Correctness".ÿ (Your
paraphrase could be interpreted as advice, which I'm sure is not
how you intended it.)
It was actually a (deliberate) "marketing trick" to provoke interest
by reducing the title to an (IMO) yet more aggravating formulation.
Yes, it was not my intention to suggest performance over correctness.
(Would anyone?)
Unfortunately, the answer to your final question is yes - and that is
one of the reasons UB can be a problem.ÿ If by "correctness" you mean
"code does what it should", people do sometimes release code that they
know has bugs, but they know that fixing them would slow down the code
- perhaps it is still good enough for their purposes at the time.
Or, the cliche: "It's not a bug, it's a feature..."
More relevant to this discussion, if by "correct" code you mean code
that has fully defined or appropriate implementation-defined behaviour
(most code does not need to be fully portable) according to the C
standards and/or platform or compiler extended semantics, then it is
absolutely the case that programmers regularly prioritise performance
over correctness by relying on UB.ÿ The result is code that works
efficiently and as intended at the time, using tools tested by the
developer.ÿ Then the UB bites the next person down the road that uses
the same code in good faith, but with a different compiler or
different options.
Well, or for example, this pile of wonk:
#define gfxedit_getu16(ptr)ÿÿÿÿÿÿ (*(vol_u16p)(ptr))
#define gfxedit_getu32(ptr)ÿÿÿÿÿÿ (*(vol_u32p)(ptr))
#define gfxedit_getu64(ptr)ÿÿÿÿÿÿ (*(vol_u64p)(ptr))
#define gfxedit_setu16(ptr,val)ÿÿ (*(vol_u16p)(ptr)=(val))
#define gfxedit_setu32(ptr,val)ÿÿ (*(vol_u32p)(ptr)=(val))
#define gfxedit_setu64(ptr,val)ÿÿ (*(vol_u64p)(ptr)=(val))
Insert other versions for different platform constraints.
But, say:
ÿ u64 gfxedit_getu64(void *ptr)
ÿÿÿ { u64 v; memcpy(&v, ptr, 8); return(v); }
Being also possible, but a significant performance penalty on some targets.
The situation is potentially much worse for big endian machines, but
these are rare at this point.
Say:
ÿ u16 gfxedit_getu16(void *ptr)
ÿÿÿ { u16 v; v=((byte *)ptr)[0]|
ÿÿÿÿÿÿÿ (((u16)((byte *)ptr)[1]))<<8); return(v); }
ÿ u32 gfxedit_getu32(void *ptr)
ÿÿÿ { u32 v; v=gfxedit_getu16(ptr)|
ÿÿÿÿÿÿÿ (((u32)gfxedit_getu16(((byte *)ptr)+2))<<16); return(v); }
ÿ u64 gfxedit_getu64(void *ptr)
ÿÿÿ { u64 v; v=gfxedit_getu32(ptr)|
ÿÿÿÿÿÿÿ (((u64)gfxedit_getu32(((byte *)ptr)+4))<<32); return(v); }
Though, for BE machines, one wouldn't want to overuse these.
But, on the relevant implementation, trying to do the latter being
around an order of magnitude slower...
So, it can all turn into a big mess of cruft and ifdef's.
The killer point, however, is when you have received some code that
you can justifiably assume is correct and well tested, but contains
reliance on how certain types of UB happen to be implemented.ÿ It is
not really any different than code that makes other undocumented
assumptions, such as implementation-dependent details, reliance on
certain files or OS features, timing requirements, etc.ÿ And compiling
with "-fwrapv -fno- strict-aliasing" (if you are using a compiler that
supports such features) will mitigate the most common cases of
reliance on UB.
In some cases, it makes sense to assume that some things formally
considered UB would be better considered as Implementation Defined.
In practice, it likely wouldn't make that big of a difference; since
many cases the compiler still treats these cases is implementation
defined rather than true UB.
But, I had seen non-zero code which had relied on the assumption of overflows carrying over consistently between global arrays.
As for the paper itself, I have a few points.
It follows the time-worn path of complaining that "modern" compilers
have distorted the "original" idea of UB and abuse it for optimisation
in ways that are dangerous to the uninitiated.ÿ First, optimisation
using UB is not new - I first used a compiler that assumed integer
overflow does not wrap over thirty years ago.ÿ Second, compilers
already default to "-O0" - if you know so little about your tools that
you can't enable warnings, you won't have optimisation either.ÿ Third,
improvements in compiler optimisation have gone hand-in-hand with
improvements in compiler static error checking.ÿ Using "-Wall" along
with optimisation will eliminate the solid majority of the risks and
worries of the author.ÿ Fourth, the development of "sanitizers" not
only allows far more UB to be found during testing, but it allows far
more bugs to be found than would be possible if the mistakes were /
not/ UB - "-fsanitize=signed-integer-overflow" catches overflow bugs
precisely because it is UB in C, whereas if it were defines as
wrapping then the mistake in the programmer's code would be correct as
far as the language is concerned.
For keeping old code working, it makes sense to assume wrapping.
But, maybe it could make sense to have a semantic distinction for cases where wrapping is assumed vs where it is likely unintentional.
My proposal, if any, would be, say:
ÿ If "signed" is used explicitly, it means wrap-on-overflow is expected.
So, say:
ÿ int i;
ÿ signed int v;
If 'i' overflows, one can question whether or not it was intended; but
if 'v' overflows, assume it is intentional (as with 'unsigned').
This being in part because an explicit "signed" keyword is uncommon to
be used with normal counting/index type variables.
Don't dereference null pointers.ÿ It's a bad idea.
Dereferencing NULL is one of those cases where the most sensible option
is to treat it as a fault...
Most sensible option is to assume that trying to dereference NULL is unintentional (given the main purpose of NULL being as a "this object doesn't exist" pointer).
But:
Assuming it doesn't happen and then removing the offending code path entirely is also bad IMO. However, turning the code-path into a break instruction or similar (following any other externally visible side
effects) would still likely be acceptable IMO (or, at least, less bad in this case than just continuing down the other path as if nothing had
ever happened).
On 22/07/2026 07:33, Janis Papanagnou wrote:
On 2026-07-22 02:26, Keith Thompson wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
There was some recent longish thread with discussions addressing
Undefined Behavior. I just stumbled across a paper from Russ Cox
on that topic with a provoking title. [...]
For those who don't follow the link, the full title of the paper is
"C and C++ Prioritize Performance over Correctness".ÿ (Your
paraphrase could be interpreted as advice, which I'm sure is not
how you intended it.)
It was actually a (deliberate) "marketing trick" to provoke interest
by reducing the title to an (IMO) yet more aggravating formulation.
Yes, it was not my intention to suggest performance over correctness.
(Would anyone?)
Unfortunately, the answer to your final question is yes -
and that is one of the reasons UB can be a problem.
[...]
You are thinking of code like :
int x = *p;
if (!p) return someone_made_a_mistake;
...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0. That
is fair enough - /if/ the target is guaranteed to have such a hardware
catch. If it is not guaranteed, then the check can't be skipped.
Even better, of course, would be a compiler warning that the
programmer is doing something silly - whether or not the check is
skipped.
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :
int x = *p;
if (!p) return someone_made_a_mistake;
...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0. That
is fair enough - /if/ the target is guaranteed to have such a hardware
catch. If it is not guaranteed, then the check can't be skipped.
Yes, it can. A conforming compiler can omit the (!p) test because the behavior is undefined, not (necessarily) because of the behavior of the target system.
Even better, of course, would be a compiler warning that the
programmer is doing something silly - whether or not the check is
skipped.
Certainly a warning would be nice. The problem is that unexpected optimizations in the presence of undefined behavior don't always occur because the compiler *knows* that the behavior is undefined.
[...]
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :
int x = *p;
if (!p) return someone_made_a_mistake;
...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0. That
is fair enough - /if/ the target is guaranteed to have such a hardware
catch. If it is not guaranteed, then the check can't be skipped.
Yes, it can. A conforming compiler can omit the (!p) test because the
behavior is undefined, not (necessarily) because of the behavior of the
target system.
Sorry, I was imprecise.
If p is a null pointer, the behavior is undefined. If p is a
valid non-null pointer, the behavior is well defined, and the
`return someone_made_a_mistake;` statement will be executed. Since
returning `someone_made_a_mistake` is a valid example of undefined
behavior, a compiler can simplify the code to an unconditional
`return someone_made_a_mistake;`.
It can't just omit checks because the behavior *might* be undefined.
Even better, of course, would be a compiler warning that the
programmer is doing something silly - whether or not the check is
skipped.
Certainly a warning would be nice. The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur
because the compiler *knows* that the behavior is undefined.
[...]
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :
int x = *p;
if (!p) return someone_made_a_mistake;
...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0. That
is fair enough - /if/ the target is guaranteed to have such a hardware >>>> catch. If it is not guaranteed, then the check can't be skipped.
Yes, it can. A conforming compiler can omit the (!p) test because the
behavior is undefined, not (necessarily) because of the behavior of the
target system.
Sorry, I was imprecise.
Sorry, I was also *wrong*.
If p is a null pointer, the behavior is undefined. If p is a
valid non-null pointer, the behavior is well defined, and the
`return someone_made_a_mistake;` statement will be executed. Since
returning `someone_made_a_mistake` is a valid example of undefined
behavior, a compiler can simplify the code to an unconditional
`return someone_made_a_mistake;`.
Correction: if p is a valid non-null pointer, the behavior is well
defined, and the `return someone_made_a_mistake;` statement will *NOT*
be executed. A conforming compiler can emit code that does nothing.
For that matter, the behavior is undefined if p is an invalid non-null pointer, so that dereferencing it has UB.
It can't just omit checks because the behavior *might* be undefined.
Even better, of course, would be a compiler warning that the
programmer is doing something silly - whether or not the check is
skipped.
Certainly a warning would be nice. The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur
because the compiler *knows* that the behavior is undefined.
On 24/07/2026 00:11, Keith Thompson wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :
int x = *p;
if (!p) return someone_made_a_mistake;
...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0. That >>>>> is fair enough - /if/ the target is guaranteed to have such a hardware >>>>> catch. If it is not guaranteed, then the check can't be skipped.
Yes, it can. A conforming compiler can omit the (!p) test because the >>>> behavior is undefined, not (necessarily) because of the behavior of the >>>> target system.
See below for more discussion.
In the case of gcc's "-fdelete-null-pointer-checks" flag, the compiler documentation says that look-after-you-leap null pointer checks can be eliminated because the compiler assumes that any dereferencing of
address zero results in a trap. The big fuss when this flag was
introduced (and enabled by default) in gcc was because the Linux
kernel contained such mistakes in the code, and by default address 0
was accessible without causing a trap.
Sorry, I was imprecise.Sorry, I was also *wrong*.
If p is a null pointer, the behavior is undefined. If p is aCorrection: if p is a valid non-null pointer, the behavior is well
valid non-null pointer, the behavior is well defined, and the
`return someone_made_a_mistake;` statement will be executed. Since
returning `someone_made_a_mistake` is a valid example of undefined
behavior, a compiler can simplify the code to an unconditional
`return someone_made_a_mistake;`.
defined, and the `return someone_made_a_mistake;` statement will *NOT*
be executed. A conforming compiler can emit code that does nothing.
(Caveat - what I am about to write is the best of my understanding,
but it might not be correct. If I am wrong, I am happy to be
corrected!)
The trouble with your argument here is that a pointer with value 0 is
not necessarily a null pointer (and I'm assuming an implementation
where null pointers have a value 0, not anything weirder than that).
Null pointers are generated from null pointer constants - 0, (void*)
0, and nullptr. If you have a pointer that happens to contain the
value 0 but it did not come from a null pointer constant, it is not a
null pointer and dereferencing it is not inherently UB. (Of course it
is still UB if it does not point to a valid object of appropriate
type.)
The evaluation of "!p" is based on a comparison of "p" to the value 0
- it will be true if p contains the value 0. (This point is perhaps
the weak link in my reasoning - maybe "!p" is only true if "p" is
actually a null pointer. However, I am confident that all real implementations, where null pointers are value 0, act by comparison of
the value.)
As far as I can see, therefore, "p" can be a valid pointer to an
object of appropriate type, not be a null pointer, and still have
value 0 and have "!p" evaluate to 1.
For that matter, the behavior is undefined if p is an invalid non-null
pointer, so that dereferencing it has UB.
Agreed.
Of course, a compiler can only optimise away UB code if it knows it is
UB - the compiler usually cannot know that a general pointer is
invalid.
It can't just omit checks because the behavior *might* be undefined.
Even better, of course, would be a compiler warning that the
programmer is doing something silly - whether or not the check is
skipped.
Certainly a warning would be nice. The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur >>>> because the compiler *knows* that the behavior is undefined.
Yes, indeed. And compilers are built up with lots of passes and do
not hold all information throughout the chain (otherwise they would be
a lot bigger, a lot slower, and use a lot more memory). Often by the
time code is eliminated or re-arranged in later passes, the original
reason is lost. Older versions of gcc had a warning you could enable
to tell you when code was eliminated - the warning was removed because
it was too noisy in the face of more advanced optimisations (like code folding, constant propagation, and function cloning), and there was no practical reliable way to give just the helpful cases. We can always
wish for better warnings, and keep asking for them from our compiler
vendors, but the compiler writers are unfortunately limited by
practical realities.
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :
int x = *p;
if (!p) return someone_made_a_mistake;
...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0. That
is fair enough - /if/ the target is guaranteed to have such a hardware
catch. If it is not guaranteed, then the check can't be skipped.
Yes, it can. A conforming compiler can omit the (!p) test because the behavior is undefined, not (necessarily) because of the behavior of the target system.
Even better, of course, would be a compiler warning that the
programmer is doing something silly - whether or not the check is
skipped.
Certainly a warning would be nice. The problem is that unexpected optimizations in the presence of undefined behavior don't always occur because the compiler *knows* that the behavior is undefined.
On 2026-07-23 23:31, Keith Thompson wrote:
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :
ÿÿÿÿint x = *p;
ÿÿÿÿif (!p) return someone_made_a_mistake;
ÿÿÿÿ...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0.ÿ That
is fair enough - /if/ the target is guaranteed to have such a hardware
catch.ÿ If it is not guaranteed, then the check can't be skipped.
Yes, it can.ÿ A conforming compiler can omit the (!p) test because the
behavior is undefined, not (necessarily) because of the behavior of the
target system.
(I've seen you posted two followups on above. - No comment on
all that.)
Even better, of course, would be a compiler warning that the
programmer is doing something silly - whether or not the check is
skipped.
Certainly a warning would be nice.ÿ The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur
because the compiler *knows* that the behavior is undefined.
This thought or reasoning sounds completely perverted. - If the
compiler *knows* that there's undefined behavior (that may lead
to non-equivalent (=illegitimate) [dangerous] transformations!)
a warning (or error message) should be *mandated* in any case!
Without such a notice any "optimizations" (based on bizarre and
hazardous assumptions) should never be performed! - Certainly a
warning would not only be "nice" but should be mandated. - YMMV.
It probably just boils down to the fact that the C-folks seem to
imply another meaning of "optimization", meaning more something
like "mutation of the code to something functionally different".
Or, as it appear to me, that (with the knowledge of the inherent
problems to overcome the issues) accommodated to the situation.
Given what "C" actually is (and what it can *not* become) I don't
think it makes sense to continue arguing about what I consider to
be just the crazy facts of ("C"-)reality.
Janis
On 24/07/2026 00:11, Keith Thompson wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :
int x = *p;
if (!p) return someone_made_a_mistake;
...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0. That >>>>> is fair enough - /if/ the target is guaranteed to have such a hardware >>>>> catch. If it is not guaranteed, then the check can't be skipped.
Yes, it can. A conforming compiler can omit the (!p) test because the >>>> behavior is undefined, not (necessarily) because of the behavior of the >>>> target system.
See below for more discussion.
In the case of gcc's "-fdelete-null-pointer-checks" flag, the compiler >documentation says that look-after-you-leap null pointer checks can be >eliminated because the compiler assumes that any dereferencing of
address zero results in a trap. The big fuss when this flag was
introduced (and enabled by default) in gcc was because the Linux kernel >contained such mistakes in the code, and by default address 0 was
accessible without causing a trap.
On 2026-07-23 23:31, Keith Thompson wrote:
Certainly a warning would be nice.ÿ The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur
because the compiler *knows* that the behavior is undefined.
This thought or reasoning sounds completely perverted. - If the
compiler *knows* that there's undefined behavior (that may lead
to non-equivalent (=illegitimate) [dangerous] transformations!)
a warning (or error message) should be *mandated* in any case!
Without such a notice any "optimizations" (based on bizarre and
hazardous assumptions) should never be performed! - Certainly a
warning would not only be "nice" but should be mandated. - YMMV.
It probably just boils down to the fact that the C-folks seem to
imply another meaning of "optimization", meaning more something
like "mutation of the code to something functionally different".
Or, as it appear to me, that (with the knowledge of the inherent
problems to overcome the issues) accommodated to the situation.
Given what "C" actually is (and what it can *not* become) I don't
think it makes sense to continue arguing about what I consider to
be just the crazy facts of ("C"-)reality.
On 2026-07-23 23:31, Keith Thompson wrote:
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :
ÿÿÿÿint x = *p;
ÿÿÿÿif (!p) return someone_made_a_mistake;
ÿÿÿÿ...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0.ÿ That
is fair enough - /if/ the target is guaranteed to have such a hardware
catch.ÿ If it is not guaranteed, then the check can't be skipped.
Yes, it can.ÿ A conforming compiler can omit the (!p) test because the
behavior is undefined, not (necessarily) because of the behavior of the
target system.
(I've seen you posted two followups on above. - No comment on
all that.)
Even better, of course, would be a compiler warning that the
programmer is doing something silly - whether or not the check is
skipped.
Certainly a warning would be nice.ÿ The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur
because the compiler *knows* that the behavior is undefined.
This thought or reasoning sounds completely perverted. - If the
compiler *knows* that there's undefined behavior (that may lead
to non-equivalent (=illegitimate) [dangerous] transformations!)
a warning (or error message) should be *mandated* in any case!
Without such a notice any "optimizations" (based on bizarre and
hazardous assumptions) should never be performed! - Certainly a
warning would not only be "nice" but should be mandated. - YMMV.
It probably just boils down to the fact that the C-folks seem to
imply another meaning of "optimization", meaning more something
like "mutation of the code to something functionally different".
Or, as it appear to me, that (with the knowledge of the inherent
problems to overcome the issues) accommodated to the situation.
Given what "C" actually is (and what it can *not* become) I don't
think it makes sense to continue arguing about what I consider to
be just the crazy facts of ("C"-)reality.
On 24/07/2026 8:47 PM, Janis Papanagnou wrote:
On 2026-07-23 23:31, Keith Thompson wrote:
Certainly a warning would be nice.ÿ The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur
because the compiler *knows* that the behavior is undefined.
This thought or reasoning sounds completely perverted. - If the
compiler *knows* that there's undefined behavior (that may lead
to non-equivalent (=illegitimate) [dangerous] transformations!)
a warning (or error message) should be *mandated* in any case!
Without such a notice any "optimizations" (based on bizarre and
hazardous assumptions) should never be performed! - Certainly a
warning would not only be "nice" but should be mandated. - YMMV.
It probably just boils down to the fact that the C-folks seem to
imply another meaning of "optimization", meaning more something
like "mutation of the code to something functionally different".
Or, as it appear to me, that (with the knowledge of the inherent
problems to overcome the issues) accommodated to the situation.
Given what "C" actually is (and what it can *not* become) I don't
think it makes sense to continue arguing about what I consider to
be just the crazy facts of ("C"-)reality.
Sounds like you want to read /What every compiler writer should know
about programmers/ by Anton Ertl, if you haven't already.ÿ Link at
ÿ https://c9x.me/compile/bib/
and more for everyone who wants to write his own C compiler.
What some people have done, including me, is to simply lose faith in
the C ISO committee, and started to write our own C compilers.
On 2026-07-23 23:31, Keith Thompson wrote:
David Brown <david.brown@hesbynett.no> writes:
[...]
You are thinking of code like :Yes, it can. A conforming compiler can omit the (!p) test because
int x = *p;
if (!p) return someone_made_a_mistake;
...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0. That
is fair enough - /if/ the target is guaranteed to have such a hardware
catch. If it is not guaranteed, then the check can't be skipped.
the
behavior is undefined, not (necessarily) because of the behavior of the
target system.
(I've seen you posted two followups on above. - No comment on
all that.)
Even better, of course, would be a compiler warning that theCertainly a warning would be nice. The problem is that unexpected
programmer is doing something silly - whether or not the check is
skipped.
optimizations in the presence of undefined behavior don't always occur
because the compiler *knows* that the behavior is undefined.
This thought or reasoning sounds completely perverted. - If the
compiler *knows* that there's undefined behavior (that may lead
to non-equivalent (=illegitimate) [dangerous] transformations!)
a warning (or error message) should be *mandated* in any case!
Without such a notice any "optimizations" (based on bizarre and
hazardous assumptions) should never be performed! - Certainly a
warning would not only be "nice" but should be mandated. - YMMV.
It probably just boils down to the fact that the C-folks seem to
imply another meaning of "optimization", meaning more something
like "mutation of the code to something functionally different".
Or, as it appear to me, that (with the knowledge of the inherent
problems to overcome the issues) accommodated to the situation.
Given what "C" actually is (and what it can *not* become) I don't
think it makes sense to continue arguing about what I consider to
be just the crazy facts of ("C"-)reality.
Sounds like you want to read /What every compiler writer should know
about programmers/ by Anton Ertl, if you haven't already. Link at
https://c9x.me/compile/bib/
and more for everyone who wants to write his own C compiler.
What some people have done, including me, is to simply lose faith in
the C ISO committee, and started to write our own C compilers. This
will make Mr. Singapore from the noise earlier very happy.
Now, I decide how to react to this /Undefined behaviour/ means, and I disagree with the people behind the "big three" C compilers.
On 24/07/2026 17:52, Johann 'Myrkraverk' Oskarsson wrote:
On 24/07/2026 8:47 PM, Janis Papanagnou wrote:
On 2026-07-23 23:31, Keith Thompson wrote:
Certainly a warning would be nice.ÿ The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur >>>> because the compiler *knows* that the behavior is undefined.
This thought or reasoning sounds completely perverted. - If the
compiler *knows* that there's undefined behavior (that may lead
to non-equivalent (=illegitimate) [dangerous] transformations!)
a warning (or error message) should be *mandated* in any case!
Without such a notice any "optimizations" (based on bizarre and
hazardous assumptions) should never be performed! - Certainly a
warning would not only be "nice" but should be mandated. - YMMV.
It probably just boils down to the fact that the C-folks seem to
imply another meaning of "optimization", meaning more something
like "mutation of the code to something functionally different".
Or, as it appear to me, that (with the knowledge of the inherent
problems to overcome the issues) accommodated to the situation.
Given what "C" actually is (and what it can *not* become) I don't
think it makes sense to continue arguing about what I consider to
be just the crazy facts of ("C"-)reality.
Sounds like you want to read /What every compiler writer should know
about programmers/ by Anton Ertl, if you haven't already.ÿ Link at
ÿÿ https://c9x.me/compile/bib/
This is an extract from that PDF:
-------------------------------
ÿ int d[16];
ÿ int SATD (void) {
ÿÿÿÿÿ int satd = 0, dd, k;
ÿÿÿÿÿ for (dd=d[k=0]; k<16; dd=d[++k]) {
ÿÿÿÿÿÿÿÿÿ satd += (dd < 0 ? -dd : dd);
ÿÿÿÿÿ }
ÿÿÿÿÿ return satd;
ÿ }
"This was ?optimized? by a pre-release of gcc-4.8 into the following infinite loop:
ÿ SATD:
ÿ .L2:
ÿ jmp .L2
What happened? The compiler assumed that no out-of-bounds access to d
would happen, and from that derived that k is at most 15 after the
access, so the following test k<16 can be ?optimized? to 1 (true),
resulting in an endless loop. Then the compiler sees that the return is
now unreachable, that satd is dead, that dd is dead, and k is dead, and optimizes the rest away."
-------------------------------
This is pretty crazy!
and more for everyone who wants to write his own C compiler.
What some people have done, including me, is to simply lose faith in
the C ISO committee, and started to write our own C compilers.
Me too. But a big constraint is the language, and also existing
practice, if the aim is to be able to compile existing code.
Because 'big' compilers are gcc are lax by default, it means poor habits
are perpetuated over the years, and your compiler therefore needs to be
lax too, although at least you can make your own stricter by default stricter.
By 'the language', I mean that C allows lots of things which in my own systems language for example, that I maintain, would be impossible to
write as they are nonsensical.
With C, what I found bizarre was that it was easy to write a C program
where with one of these results when compiled:
* It passes with no errors
* It passes but with warnings (and still generates an executable)
* It fails with errors
All depending on which options have been chosen. Was the program correct
or not; who knows? I think here it is the language standard giving too
much scope to compilers, in part in order to be able to compile poor
quality legacy code.
On 24/07/2026 17:52, Johann 'Myrkraverk' Oskarsson wrote:
ÿ https://c9x.me/compile/bib/
This is an extract from that PDF:
-------------------------------
int d[16];
int SATD (void) {
int satd = 0, dd, k;
for (dd=d[k=0]; k<16; dd=d[++k]) {
satd += (dd < 0 ? -dd : dd);
}
return satd;
}
"This was ?optimized? by a pre-release of gcc-4.8 into the following
infinite loop:
SATD:
.L2:
jmp .L2
What happened? The compiler assumed that no out-of-bounds access to
d would happen, and from that derived that k is at most 15 after the
access, so the following test k<16 can be ?optimized? to 1 (true),
resulting in an endless loop. Then the compiler sees that the return
is now unreachable, that satd is dead, that dd is dead, and k is
dead, and optimizes the rest away."
-------------------------------
This is pretty crazy!
On Fri, 24 Jul 2026 23:08:47 +0100, bart wrote:
On 24/07/2026 17:52, Johann 'Myrkraverk' Oskarsson wrote:
ÿ https://c9x.me/compile/bib/
This is an extract from that PDF:
-------------------------------
int d[16];
int SATD (void) {
int satd = 0, dd, k;
for (dd=d[k=0]; k<16; dd=d[++k]) {
satd += (dd < 0 ? -dd : dd);
}
return satd;
}
"This was ?optimized? by a pre-release of gcc-4.8 into the following
infinite loop:
SATD:
.L2:
jmp .L2
What happened? The compiler assumed that no out-of-bounds access to
d would happen, and from that derived that k is at most 15 after the
access, so the following test k<16 can be ?optimized? to 1 (true),
resulting in an endless loop. Then the compiler sees that the return
is now unreachable, that satd is dead, that dd is dead, and k is
dead, and optimizes the rest away."
-------------------------------
This is pretty crazy!
Would you rather it segfaulted instead?
Because I can?t see any other reasonable interpretation of that code.
The code is perfectly reasonable.
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
[...]
Sounds like you want to read /What every compiler writer should know
about programmers/ by Anton Ertl, if you haven't already. Link at
https://c9x.me/compile/bib/
and more for everyone who wants to write his own C compiler.
I haven't read it yet, but I intend to (even though I'm not planning to
write my own C compiler).
What some people have done, including me, is to simply lose faith in
the C ISO committee, and started to write our own C compilers. This
will make Mr. Singapore from the noise earlier very happy.
Now, I decide how to react to this /Undefined behaviour/ means, and I
disagree with the people behind the "big three" C compilers.
Do you mean that your own compiler will not conform to the C
standard, or merely that it will conform in different ways than the
"big three"?
If you think a compiler should not generate code based on the
assumption of defined behavior (for example, that it should generate
code for a statement even if that statement can be executed only if
undefined behavior has already occurred), that's fine, and nothing
in the C standard prevents it. There are plenty of things the C
standard permits compilers but does not require compilers to do.
On Sat, 25 Jul 2026 15:20:21 +0800, Johann 'Myrkraverk' Oskarsson
wrote:
The code is perfectly reasonable.
If the code were ?perfectly reasonable?, then there would be a
compiler bug. There isn?t.
On 24/07/2026 17:52, Johann 'Myrkraverk' Oskarsson wrote:
On 24/07/2026 8:47 PM, Janis Papanagnou wrote:
On 2026-07-23 23:31, Keith Thompson wrote:
Certainly a warning would be nice.ÿ The problem is that unexpected
optimizations in the presence of undefined behavior don't always occur >>>> because the compiler *knows* that the behavior is undefined.
This thought or reasoning sounds completely perverted. - If the
compiler *knows* that there's undefined behavior (that may lead
to non-equivalent (=illegitimate) [dangerous] transformations!)
a warning (or error message) should be *mandated* in any case!
Without such a notice any "optimizations" (based on bizarre and
hazardous assumptions) should never be performed! - Certainly a
warning would not only be "nice" but should be mandated. - YMMV.
It probably just boils down to the fact that the C-folks seem to
imply another meaning of "optimization", meaning more something
like "mutation of the code to something functionally different".
Or, as it appear to me, that (with the knowledge of the inherent
problems to overcome the issues) accommodated to the situation.
Given what "C" actually is (and what it can *not* become) I don't
think it makes sense to continue arguing about what I consider to
be just the crazy facts of ("C"-)reality.
Sounds like you want to read /What every compiler writer should know
about programmers/ by Anton Ertl, if you haven't already.ÿ Link at
ÿÿ https://c9x.me/compile/bib/
This is an extract from that PDF:
-------------------------------
ÿ int d[16];
ÿ int SATD (void) {
ÿÿÿÿÿ int satd = 0, dd, k;
ÿÿÿÿÿ for (dd=d[k=0]; k<16; dd=d[++k]) {
ÿÿÿÿÿÿÿÿÿ satd += (dd < 0 ? -dd : dd);
ÿÿÿÿÿ }
ÿÿÿÿÿ return satd;
ÿ }
"This was ?optimized? by a pre-release of gcc-4.8 into the following infinite loop:
ÿ SATD:
ÿ .L2:
ÿ jmp .L2
What happened? The compiler assumed that no out-of-bounds access to d
would happen, and from that derived that k is at most 15 after the
access, so the following test k<16 can be ?optimized? to 1 (true),
resulting in an endless loop. Then the compiler sees that the return is
now unreachable, that satd is dead, that dd is dead, and k is dead, and optimizes the rest away."
-------------------------------
This is pretty crazy!
On 25/07/2026 3:24 PM, Lawrence D?Oliveiro wrote:
On Sat, 25 Jul 2026 15:20:21 +0800, Johann 'Myrkraverk' Oskarsson
wrote:
The code is perfectly reasonable.
If the code were ?perfectly reasonable?, then there would be a
compiler bug. There isn?t.
That's a simple matter of debate.ÿ I make the point it's a compiler
bug.ÿ You don't, so you must be a member of the "big three" developer
teams.ÿ Therefore all your opinions on my way of compiling C are
completely invalid.
On 24/07/2026 00:11, Keith Thompson wrote:
The trouble with your argument here is that a pointer with value 0 is
not necessarily a null pointer (and I'm assuming an implementation where null pointers have a value 0, not anything weirder than that).
Null pointers are generated from null pointer constants - 0, (void*) 0,
and nullptr. If you have a pointer that happens to contain the value 0
but it did not come from a null pointer constant, it is not a null
pointer and dereferencing it is not inherently UB. (Of course it is
still UB if it does not point to a valid object of appropriate type.)
The evaluation of "!p" is based on a comparison of "p" to the value 0 -
it will be true if p contains the value 0. (This point is perhaps the
weak link in my reasoning - maybe "!p" is only true if "p" is actually a null pointer. However, I am confident that all real implementations,
where null pointers are value 0, act by comparison of the value.)
As far as I can see, therefore, "p" can be a valid pointer to an object
of appropriate type, not be a null pointer, and still have value 0 and
have "!p" evaluate to 1.
On 25/07/2026 3:24 PM, Lawrence D?Oliveiro wrote:
On Sat, 25 Jul 2026 15:20:21 +0800, Johann 'Myrkraverk' Oskarsson
wrote:
The code is perfectly reasonable.
If the code were ?perfectly reasonable?, then there would be a
compiler bug. There isn?t.
That's a simple matter of debate.
On 25/07/2026 7:29 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
[...]
Sounds like you want to read /What every compiler writer should knowI haven't read it yet, but I intend to (even though I'm not planning
about programmers/ by Anton Ertl, if you haven't already. Link at
https://c9x.me/compile/bib/
and more for everyone who wants to write his own C compiler.
to
write my own C compiler).
What some people have done, including me, is to simply lose faith inDo you mean that your own compiler will not conform to the C
the C ISO committee, and started to write our own C compilers. This
will make Mr. Singapore from the noise earlier very happy.
Now, I decide how to react to this /Undefined behaviour/ means, and I
disagree with the people behind the "big three" C compilers.
standard, or merely that it will conform in different ways than the
"big three"?
I'm going to leave my disagreement with the "big three" as a surprise
until release.
If you think a compiler should not generate code based on the
assumption of defined behavior (for example, that it should generate
code for a statement even if that statement can be executed only if
undefined behavior has already occurred), that's fine, and nothing
in the C standard prevents it. There are plenty of things the C
standard permits compilers but does not require compilers to do.
Indeed. If I remember correctly, loading the source code is
/implementation defined/, so the compiler can perfectly well
read NNTP instead of files, and strictly parse only code posted
to comp.lang.c.
I wonder if Mr. Singapore thought of that angle?
On Sat, 25 Jul 2026 15:30:09 +0800, Johann 'Myrkraverk' Oskarsson wrote:
On 25/07/2026 3:24 PM, Lawrence D?Oliveiro wrote:
On Sat, 25 Jul 2026 15:20:21 +0800, Johann 'Myrkraverk' Oskarsson
wrote:
The code is perfectly reasonable.
If the code were ?perfectly reasonable?, then there would be a
compiler bug. There isn?t.
That's a simple matter of debate.
Back in the early days of computing, we had a principle called ?GIGO?
-- ?Garbage In, Garbage Out?. That meant that, if you fed crap into
the computer, don?t expect to get meaningful results out.
Do they still teach that in ?IT Studies?, or ?Digital Technologies?,
or whatever passes for CompSci courses these days?
On 25/07/2026 09:30, Johann 'Myrkraverk' Oskarsson wrote:
On 25/07/2026 3:24 PM, Lawrence D?Oliveiro wrote:
On Sat, 25 Jul 2026 15:20:21 +0800, Johann 'Myrkraverk' Oskarsson
wrote:
The code is perfectly reasonable.
If the code were ?perfectly reasonable?, then there would be a
compiler bug. There isn?t.
That's a simple matter of debate.ÿ I make the point it's a compiler
bug.ÿ You don't, so you must be a member of the "big three" developer
teams.ÿ Therefore all your opinions on my way of compiling C are
completely invalid.
It is not a matter of debate - the code is buggy.ÿ But it's not entirely obvious that it is buggy - the folks who wrote the code were pretty good
and experienced C programmers, and /they/ didn't notice the bug.
Lawrence is not (AFAIK) a developer for the "big three" compiler groups,
but perhaps he works in support for a big IT company - his answer was technically entirely correct, while being entirely unhelpful.
If you look carefully at the code again, consider the final iteration -
when k is 15 (the test "k < 16" still passes) there is the expression
"dd = d[++k]".ÿ "k" is incremented to 16, and then we read "d[16]" to
assign to "dd".ÿ But "d[16]" does not exist - the array "d" runs from
"d[0]" to "d[15]".ÿ Attempting to read "d[16]" is an out-of-bounds
access, and that's UB.ÿ It's a bug.
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 25/07/2026 7:29 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
[...]
Sounds like you want to read /What every compiler writer should knowI haven't read it yet, but I intend to (even though I'm not planning
about programmers/ by Anton Ertl, if you haven't already. Link at
https://c9x.me/compile/bib/
and more for everyone who wants to write his own C compiler.
to
write my own C compiler).
What some people have done, including me, is to simply lose faith inDo you mean that your own compiler will not conform to the C
the C ISO committee, and started to write our own C compilers. This
will make Mr. Singapore from the noise earlier very happy.
Now, I decide how to react to this /Undefined behaviour/ means, and I
disagree with the people behind the "big three" C compilers.
standard, or merely that it will conform in different ways than the
"big three"?
I'm going to leave my disagreement with the "big three" as a surprise
until release.
That's nice.
If you're not going to answer my question, why bother posting a followup
at all?
If you think a compiler should not generate code based on the
assumption of defined behavior (for example, that it should generate
code for a statement even if that statement can be executed only if
undefined behavior has already occurred), that's fine, and nothing
in the C standard prevents it. There are plenty of things the C
standard permits compilers but does not require compilers to do.
Indeed. If I remember correctly, loading the source code is
/implementation defined/, so the compiler can perfectly well
read NNTP instead of files, and strictly parse only code posted
to comp.lang.c.
That's completely irrelevant to what I wrote.
I wonder if Mr. Singapore thought of that angle?
Who?
On 25/07/2026 4:37 PM, David Brown wrote:
On 25/07/2026 09:30, Johann 'Myrkraverk' Oskarsson wrote:
On 25/07/2026 3:24 PM, Lawrence D?Oliveiro wrote:
On Sat, 25 Jul 2026 15:20:21 +0800, Johann 'Myrkraverk' Oskarsson
wrote:
The code is perfectly reasonable.
If the code were ?perfectly reasonable?, then there would be a
compiler bug. There isn?t.
That's a simple matter of debate.ÿ I make the point it's a compiler
bug.ÿ You don't, so you must be a member of the "big three" developer
teams.ÿ Therefore all your opinions on my way of compiling C are
completely invalid.
It is not a matter of debate - the code is buggy.ÿ But it's not
entirely obvious that it is buggy - the folks who wrote the code were
pretty good and experienced C programmers, and /they/ didn't notice
the bug. Lawrence is not (AFAIK) a developer for the "big three"
compiler groups, but perhaps he works in support for a big IT company
- his answer was technically entirely correct, while being entirely
unhelpful.
Don't worry about Lawrence.ÿ I just told him to buy CorelDRAW in comp. unix.shell.ÿ He's being very stubborn about being unhelpful.ÿ I'm
enjoying teasing him about it.ÿ He'll learn eventually that I don't
care at all about his opinions about anything.
If you look carefully at the code again, consider the final iteration
- when k is 15 (the test "k < 16" still passes) there is the
expression "dd = d[++k]".ÿ "k" is incremented to 16, and then we read
"d[16]" to assign to "dd".ÿ But "d[16]" does not exist - the array "d"
runs from "d[0]" to "d[15]".ÿ Attempting to read "d[16]" is an out-of-
bounds access, and that's UB.ÿ It's a bug.
Right, I did not notice that; thank you.ÿ I'll add it as a test case
for my compiler.ÿ I'm still working on preliminaries, so it might take
a year or two, or a decade before I'll show how I treat this code.
On 23/07/2026 01:08, BGB wrote:
On 7/22/2026 3:41 AM, David Brown wrote:
On 22/07/2026 07:33, Janis Papanagnou wrote:
On 2026-07-22 02:26, Keith Thompson wrote:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
There was some recent longish thread with discussions addressing
Undefined Behavior. I just stumbled across a paper from Russ Cox
on that topic with a provoking title. [...]
For those who don't follow the link, the full title of the paper is
"C and C++ Prioritize Performance over Correctness".ÿ (Your
paraphrase could be interpreted as advice, which I'm sure is not
how you intended it.)
It was actually a (deliberate) "marketing trick" to provoke interest
by reducing the title to an (IMO) yet more aggravating formulation.
Yes, it was not my intention to suggest performance over correctness.
(Would anyone?)
Unfortunately, the answer to your final question is yes - and that is
one of the reasons UB can be a problem.ÿ If by "correctness" you mean
"code does what it should", people do sometimes release code that
they know has bugs, but they know that fixing them would slow down
the code - perhaps it is still good enough for their purposes at the
time.
Or, the cliche: "It's not a bug, it's a feature..."
Possibly.
But it is always important to remember that different types of software require different levels of quality control - it can be acceptable to
have known bugs ("undocumented features" or "undocumented limitations")
in some software.ÿ If video games were coded with the same level of care used for jet engine controllers, they would never make it to market.
More relevant to this discussion, if by "correct" code you mean code
that has fully defined or appropriate implementation-defined
behaviour (most code does not need to be fully portable) according to
the C standards and/or platform or compiler extended semantics, then
it is absolutely the case that programmers regularly prioritise
performance over correctness by relying on UB.ÿ The result is code
that works efficiently and as intended at the time, using tools
tested by the developer.ÿ Then the UB bites the next person down the
road that uses the same code in good faith, but with a different
compiler or different options.
Well, or for example, this pile of wonk:
#define gfxedit_getu16(ptr)ÿÿÿÿÿÿ (*(vol_u16p)(ptr))
#define gfxedit_getu32(ptr)ÿÿÿÿÿÿ (*(vol_u32p)(ptr))
#define gfxedit_getu64(ptr)ÿÿÿÿÿÿ (*(vol_u64p)(ptr))
#define gfxedit_setu16(ptr,val)ÿÿ (*(vol_u16p)(ptr)=(val))
#define gfxedit_setu32(ptr,val)ÿÿ (*(vol_u32p)(ptr)=(val))
#define gfxedit_setu64(ptr,val)ÿÿ (*(vol_u64p)(ptr)=(val))
Insert other versions for different platform constraints.
While details of volatile accesses are implementation-dependent, they
can be a successful way of accessing the underlying memory for data.
It's easy to get things wrong, however, if you are not consistent about
it.ÿ And too much use can lead to inefficiencies as well.
But, say:
ÿÿ u64 gfxedit_getu64(void *ptr)
ÿÿÿÿ { u64 v; memcpy(&v, ptr, 8); return(v); }
Being also possible, but a significant performance penalty on some
targets.
That's the challenge.ÿ memcpy() is often a good, safe and correct way to access data as though it were a different type, but not all compilers optimise it well in such cases.ÿ Making code that is correct on a range
of implementations, and also efficient on a range of implementations, is
the hard part.ÿ So sometimes people end up with code that is efficienct
on the implementations they use, works on those, but is reliant on UB
and fails on other implementations.
The situation is potentially much worse for big endian machines, but
these are rare at this point.
Say:
ÿÿ u16 gfxedit_getu16(void *ptr)
ÿÿÿÿ { u16 v; v=((byte *)ptr)[0]|
ÿÿÿÿÿÿÿÿ (((u16)((byte *)ptr)[1]))<<8); return(v); }
ÿÿ u32 gfxedit_getu32(void *ptr)
ÿÿÿÿ { u32 v; v=gfxedit_getu16(ptr)|
ÿÿÿÿÿÿÿÿ (((u32)gfxedit_getu16(((byte *)ptr)+2))<<16); return(v); }
ÿÿ u64 gfxedit_getu64(void *ptr)
ÿÿÿÿ { u64 v; v=gfxedit_getu32(ptr)|
ÿÿÿÿÿÿÿÿ (((u64)gfxedit_getu32(((byte *)ptr)+4))<<32); return(v); }
Though, for BE machines, one wouldn't want to overuse these.
On good modern compilers, that sort of thing is usually fine, and you
would expect it to reduce to a single "load 64-bit with endian swap" instruction or minimal similar sequence.
As I see it, the main problem of UB and compilers is /not/ as commonly believed, overly-enthusiastic compilers that optimise on assumptions
about UB.ÿ The problem is poorer or older compilers that don't optimise
the correct code well, forcing people who need efficient results to
spend a lot of effort finding alternative solutions that might be
reliant on UB.ÿ It also doesn't help that some developers don't enable optimisation or otherwise fail to use their tools well.
<snip code>
But, on the relevant implementation, trying to do the latter being
around an order of magnitude slower...
It sounds like it might make more sense to write improved memcpy, memove
and memset implementations for the implementation in question!
So, it can all turn into a big mess of cruft and ifdef's.
Maximum efficiency is /always/ going to be implementation and target dependent.ÿ It is fine, for low-level functions where the speed matters,
to have a variety of implementations tuned to different targets, with fall-back to something that is correct portable code.ÿ The use of
#ifdef's (or separate files) means you can also happily use compiler- specific extensions, pragmas, attributes, builtins, etc., or even inline assembly.
The killer point, however, is when you have received some code that
you can justifiably assume is correct and well tested, but contains
reliance on how certain types of UB happen to be implemented.ÿ It is
not really any different than code that makes other undocumented
assumptions, such as implementation-dependent details, reliance on
certain files or OS features, timing requirements, etc.ÿ And
compiling with "-fwrapv -fno- strict-aliasing" (if you are using a
compiler that supports such features) will mitigate the most common
cases of reliance on UB.
In some cases, it makes sense to assume that some things formally
considered UB would be better considered as Implementation Defined.
No, never.ÿ You might want that to be the case (and I am sure we all
have our personal favourite UB's from the C standards that we would
rather have as IB, or unspecified, or the up-coming "erroneous
behaviour" - though we would not agree entirely on which UB's).ÿ But you can't just say "I think left-shift of negative numbers should be IB" and assume it to be the case!
In practice, it likely wouldn't make that big of a difference; since
many cases the compiler still treats these cases is implementation
defined rather than true UB.
Particular implementations can, of course, document the way a particular type of UB is implemented - then when you are using that compiler, you
can rely on that behaviour.
Relying on undocumented treatment of UB is where you get in trouble. The code might work as you want with this version of the compiler, and fail
with the next version.
But, I had seen non-zero code which had relied on the assumption of
overflows carrying over consistently between global arrays.
Relying on the details of how generated assembly is organised is a high- risk game.ÿ I have had occasions when I needed to do so, but it's
tightly tied to exact tool versions and flags, and needs checking for
every run of the build.
As for the paper itself, I have a few points.
It follows the time-worn path of complaining that "modern" compilers
have distorted the "original" idea of UB and abuse it for
optimisation in ways that are dangerous to the uninitiated.ÿ First,
optimisation using UB is not new - I first used a compiler that
assumed integer overflow does not wrap over thirty years ago.
Second, compilers already default to "-O0" - if you know so little
about your tools that you can't enable warnings, you won't have
optimisation either.ÿ Third, improvements in compiler optimisation
have gone hand-in-hand with improvements in compiler static error
checking.ÿ Using "-Wall" along with optimisation will eliminate the
solid majority of the risks and worries of the author.ÿ Fourth, the
development of "sanitizers" not only allows far more UB to be found
during testing, but it allows far more bugs to be found than would be
possible if the mistakes were / not/ UB - "-fsanitize=signed-integer-
overflow" catches overflow bugs precisely because it is UB in C,
whereas if it were defines as wrapping then the mistake in the
programmer's code would be correct as far as the language is concerned.
For keeping old code working, it makes sense to assume wrapping.
To be clear - you mean that if you don't know the old code, it makes
sense to assume the code might rely on wrapping behaviour, and use appropriate compiler flags (or add appropriate pragmas) ?
But, maybe it could make sense to have a semantic distinction for
cases where wrapping is assumed vs where it is likely unintentional.
My proposal, if any, would be, say:
ÿÿ If "signed" is used explicitly, it means wrap-on-overflow is expected.
That makes no sense at all.
So, say:
ÿÿ int i;
ÿÿ signed int v;
If 'i' overflows, one can question whether or not it was intended; but
if 'v' overflows, assume it is intentional (as with 'unsigned').
You can't just invent a personal convention like that - at least, not if
you ever use other people's code or compilers written by someone else.
You can't change the meaning of existing code.
If C were ever to have support for something akin to that, I would
expect to see new names involved.ÿ It would be "_Wrapping int v;", or
types "int_wrapping32_t" in <stdint.h>.
More likely, functions like "wrapping_add" could be added to <stdckdint.h>.
This being in part because an explicit "signed" keyword is uncommon to
be used with normal counting/index type variables.
Some people use it - and they do not do so with the expectation of
different semantics than when it is omitted.ÿ Some people write "signed" rather than "int" for declarations.
Don't dereference null pointers.ÿ It's a bad idea.
Dereferencing NULL is one of those cases where the most sensible
option is to treat it as a fault...
It's a bug.
Most sensible option is to assume that trying to dereference NULL is
unintentional (given the main purpose of NULL being as a "this object
doesn't exist" pointer).
Yes.
There is the exception that on some hardware, 0 is a valid memory
address and you might want to access it.ÿ On microcontrollers, it is not uncommon for code flash to start there, so something like an integrity
check of the program would have to read that address.ÿ Then you are dereferencing a pointer that happens to have value 0, rather than a null pointer.
But:
Assuming it doesn't happen and then removing the offending code path
entirely is also bad IMO. However, turning the code-path into a break
instruction or similar (following any other externally visible side
effects) would still likely be acceptable IMO (or, at least, less bad
in this case than just continuing down the other path as if nothing
had ever happened).
You are thinking of code like :
ÿÿÿÿint x = *p;
ÿÿÿÿif (!p) return someone_made_a_mistake;
ÿÿÿÿ...
Some compilers will skip the check here, because they assume the
hardware / OS will have caught the attempt to access address 0.ÿ That is fair enough - /if/ the target is guaranteed to have such a hardware
catch.ÿ If it is not guaranteed, then the check can't be skipped.ÿ Even better, of course, would be a compiler warning that the programmer is
doing something silly - whether or not the check is skipped.
I agree that sometimes a "break" (or "trap", or similar) instruction can
be a good thing for compilers to generate when they are clearly
executing undefined behaviour.ÿ But I am not convinced it is always a
good idea (I would not want it for this case), and it would be hard to specify and guarantee the circumstances without it ruining the
possibility of optimisation from assuming code has defined behaviour.
Compilers with sanitizers do support such traps - gcc with the options "-fsanitize-trap=all -fsanitize=null" will trap if the pointer "p" is
null in this code.
On 7/26/2026 11:35 AM, Johann 'Myrkraverk' Oskarsson wrote:
On 25/07/2026 4:37 PM, David Brown wrote:
On 25/07/2026 09:30, Johann 'Myrkraverk' Oskarsson wrote:
On 25/07/2026 3:24 PM, Lawrence D?Oliveiro wrote:
On Sat, 25 Jul 2026 15:20:21 +0800, Johann 'Myrkraverk' Oskarsson
wrote:
The code is perfectly reasonable.
If the code were ?perfectly reasonable?, then there would be a
compiler bug. There isn?t.
That's a simple matter of debate.ÿ I make the point it's a compiler
bug.ÿ You don't, so you must be a member of the "big three" developer
teams.ÿ Therefore all your opinions on my way of compiling C are
completely invalid.
It is not a matter of debate - the code is buggy.ÿ But it's not
entirely obvious that it is buggy - the folks who wrote the code were
pretty good and experienced C programmers, and /they/ didn't notice
the bug. Lawrence is not (AFAIK) a developer for the "big three"
compiler groups, but perhaps he works in support for a big IT company
- his answer was technically entirely correct, while being entirely
unhelpful.
Don't worry about Lawrence.ÿ I just told him to buy CorelDRAW in comp.
unix.shell.ÿ He's being very stubborn about being unhelpful.ÿ I'm
enjoying teasing him about it.ÿ He'll learn eventually that I don't
care at all about his opinions about anything.
If you look carefully at the code again, consider the final iteration
- when k is 15 (the test "k < 16" still passes) there is the
expression "dd = d[++k]".ÿ "k" is incremented to 16, and then we read
"d[16]" to assign to "dd".ÿ But "d[16]" does not exist - the array
"d" runs from "d[0]" to "d[15]".ÿ Attempting to read "d[16]" is an
out-of- bounds access, and that's UB.ÿ It's a bug.
Right, I did not notice that; thank you.ÿ I'll add it as a test case
for my compiler.ÿ I'm still working on preliminaries, so it might take
a year or two, or a decade before I'll show how I treat this code.
Well, decided to see what my compiler would do...
So, to recap:
int d[16];
int SATD (void) {
ÿÿÿÿint satd = 0, dd, k;
ÿÿÿÿfor (dd=d[k=0]; k<16; dd=d[++k]) {
ÿÿÿÿÿÿÿ satd += (dd < 0 ? -dd : dd);
ÿÿÿÿ}
ÿÿÿÿreturn satd;
}
Gives:
SATD:
// tst_random1.c:2ÿÿ int SATD (void) {
ÿ ADDÿÿÿÿÿÿÿÿÿ RD0, RQ0, RD13
// tst_random1.c:4ÿÿ for (dd=d[k=0]; k<16; dd=d[++k]) {
ÿ ADDÿÿÿÿÿÿÿÿÿ RD0, RQ0, RD12
ÿ MOVÿÿÿÿÿÿÿÿÿ d, RQ11
ÿ MOV.Lÿÿÿÿÿÿÿ (RQ11, 0), RD10
.L00800000:
ÿ MOVÿÿÿÿÿÿÿÿÿ 16, RD11
ÿ BRGE.Lÿÿÿÿÿÿ RD11, RD12, .L00800002
ÿ BRGE.Lÿÿÿÿÿÿ R0, RD10, .L00800003
ÿ RSUBS.Lÿÿÿÿÿ RD10, 0, RQ11
ÿ ADDÿÿÿÿÿÿÿÿÿ RQ11, RQ0, RQ17
ÿ BSRÿÿÿÿÿÿÿÿÿ .L00800004, R0
.L00800003:
ÿ ADDÿÿÿÿÿÿÿÿÿ RD10, RQ0, RQ17
.L00800004:
ÿ ADDS.Lÿÿÿÿÿÿ RD13, RQ17, RD13
ÿ ADDS.Lÿÿÿÿÿÿ RD12, 1, RD12
ÿ MOVÿÿÿÿÿÿÿÿÿ d, RQ16
ÿ MOV.Lÿÿÿÿÿÿÿ (RQ16, RD12), RD10
ÿ BSRÿÿÿÿÿÿÿÿÿ .L00800000, R0
.L00800002:
// tst_random1.c:6ÿÿ }
ÿ ADDS.Lÿÿÿÿÿÿ RD13, RQ0, RD10
.L00C000F9:
ÿ JSRÿÿÿÿÿÿÿÿÿ R1, 0, R0
Not exactly the way that is good to write ASM, just what my compiler
outputs when dumping ASM...
Decided not to really try to explain it, or what sort of ISA it is
aiming for.
Not the cleverest compiler around either, but gets the job done...
On 26/07/2026 18:39, BGB wrote:
On 7/26/2026 11:35 AM, Johann 'Myrkraverk' Oskarsson wrote:
On 25/07/2026 4:37 PM, David Brown wrote:
On 25/07/2026 09:30, Johann 'Myrkraverk' Oskarsson wrote:
On 25/07/2026 3:24 PM, Lawrence D?Oliveiro wrote:
On Sat, 25 Jul 2026 15:20:21 +0800, Johann 'Myrkraverk' Oskarsson
wrote:
The code is perfectly reasonable.
If the code were ?perfectly reasonable?, then there would be a
compiler bug. There isn?t.
That's a simple matter of debate.ÿ I make the point it's a compiler
bug.ÿ You don't, so you must be a member of the "big three" developer >>>>> teams.ÿ Therefore all your opinions on my way of compiling C are
completely invalid.
It is not a matter of debate - the code is buggy.ÿ But it's not
entirely obvious that it is buggy - the folks who wrote the code
were pretty good and experienced C programmers, and /they/ didn't
notice the bug. Lawrence is not (AFAIK) a developer for the "big
three" compiler groups, but perhaps he works in support for a big IT
company - his answer was technically entirely correct, while being
entirely unhelpful.
Don't worry about Lawrence.ÿ I just told him to buy CorelDRAW in comp.
unix.shell.ÿ He's being very stubborn about being unhelpful.ÿ I'm
enjoying teasing him about it.ÿ He'll learn eventually that I don't
care at all about his opinions about anything.
If you look carefully at the code again, consider the final
iteration - when k is 15 (the test "k < 16" still passes) there is
the expression "dd = d[++k]".ÿ "k" is incremented to 16, and then we
read "d[16]" to assign to "dd".ÿ But "d[16]" does not exist - the
array "d" runs from "d[0]" to "d[15]".ÿ Attempting to read "d[16]"
is an out-of- bounds access, and that's UB.ÿ It's a bug.
Right, I did not notice that; thank you.ÿ I'll add it as a test case
for my compiler.ÿ I'm still working on preliminaries, so it might take
a year or two, or a decade before I'll show how I treat this code.
Well, decided to see what my compiler would do...
So, to recap:
int d[16];
int SATD (void) {
ÿÿÿÿÿint satd = 0, dd, k;
ÿÿÿÿÿfor (dd=d[k=0]; k<16; dd=d[++k]) {
ÿÿÿÿÿÿÿÿ satd += (dd < 0 ? -dd : dd);
ÿÿÿÿÿ}
ÿÿÿÿÿreturn satd;
}
Gives:
SATD:
// tst_random1.c:2ÿÿ int SATD (void) {
ÿÿ ADDÿÿÿÿÿÿÿÿÿ RD0, RQ0, RD13
// tst_random1.c:4ÿÿ for (dd=d[k=0]; k<16; dd=d[++k]) {
ÿÿ ADDÿÿÿÿÿÿÿÿÿ RD0, RQ0, RD12
ÿÿ MOVÿÿÿÿÿÿÿÿÿ d, RQ11
ÿÿ MOV.Lÿÿÿÿÿÿÿ (RQ11, 0), RD10
.L00800000:
ÿÿ MOVÿÿÿÿÿÿÿÿÿ 16, RD11
ÿÿ BRGE.Lÿÿÿÿÿÿ RD11, RD12, .L00800002
ÿÿ BRGE.Lÿÿÿÿÿÿ R0, RD10, .L00800003
ÿÿ RSUBS.Lÿÿÿÿÿ RD10, 0, RQ11
ÿÿ ADDÿÿÿÿÿÿÿÿÿ RQ11, RQ0, RQ17
ÿÿ BSRÿÿÿÿÿÿÿÿÿ .L00800004, R0
.L00800003:
ÿÿ ADDÿÿÿÿÿÿÿÿÿ RD10, RQ0, RQ17
.L00800004:
ÿÿ ADDS.Lÿÿÿÿÿÿ RD13, RQ17, RD13
ÿÿ ADDS.Lÿÿÿÿÿÿ RD12, 1, RD12
ÿÿ MOVÿÿÿÿÿÿÿÿÿ d, RQ16
ÿÿ MOV.Lÿÿÿÿÿÿÿ (RQ16, RD12), RD10
ÿÿ BSRÿÿÿÿÿÿÿÿÿ .L00800000, R0
.L00800002:
// tst_random1.c:6ÿÿ }
ÿÿ ADDS.Lÿÿÿÿÿÿ RD13, RQ0, RD10
.L00C000F9:
ÿÿ JSRÿÿÿÿÿÿÿÿÿ R1, 0, R0
Not exactly the way that is good to write ASM, just what my compiler
outputs when dumping ASM...
Decided not to really try to explain it, or what sort of ISA it is
aiming for.
Not the cleverest compiler around either, but gets the job done...
Your code is a lot shorter than mine: 18 executable instrs versus 31 for mine which is for x64. Although 6 are to save/restore non-vol regs, and
2 for a stack-frame which doesn't appear to be needed.
It does nothing at all about trying to detect possible out-of-bounds
errors.
SUBS.L R0, R10, R11 //SUBW X11, X0, X10SATD:
ADD RD0, RQ0, RD13 //LI X13, 0
ADD RD0, RQ0, RD12 //LI X12, 0
MOV d, RQ16 //LA X16, d / ~ AUIPC+ADDI
MOV.L (RQ16, 0), RD10 //LW X10, 0(X16)
.L00800000:
MOV 16, RD11 //ADDI X11, X0, 16
BRGE.L RD11, RD12, .L00800002 //BGE X12, X11, .L00800002
More drastic reorg could save a few more instrs, but, ...ADDS.L RD13, RQ17, RD13 //ADDW X13, X17, X17
ADDS.L RD12, 1, RD12 //ADDIW X12, X12, 1
MOV.L (RQ16, RD12), RD10 //-
BSR .L00800000, R0 //J .L00800000
.L00800002:
ADDS.L RD13, RQ0, RD10 //ADDW X10, X13, X0
JSR R1, 0, R0 //RTS
gcc-O2 does it in 13, but -O3 is longer due to using SIMD and no looping.
However I then tried it in my systems language (an equivalent program)
and there it was only 22 instruction, shown below. More amenable
language features can help! Here also there is no out-of-bounds error.
SATD::ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ # (uses non-standard reg names: A/D = 32/64 bits)
ÿÿÿ R.satd = A3
ÿÿÿ R.dd = A4
ÿÿÿ R.k = A5
ÿÿÿ pushÿÿÿÿÿ D3
ÿÿÿ pushÿÿÿÿÿ D4
ÿÿÿ pushÿÿÿÿÿ D5
ÿÿÿ subÿÿÿÿÿÿ Dsp,ÿÿÿ 16
;---------------
ÿÿÿ xorÿÿÿÿÿÿ R.satd,ÿÿÿ R.satd
ÿÿÿ xorÿÿÿÿÿÿ A0,ÿÿÿ A0
ÿÿÿ movÿÿÿÿÿÿ R.k,ÿÿÿ A0
ÿÿÿ movsxÿÿÿÿ D1,ÿÿÿ A0
ÿÿÿ leaÿÿÿÿÿÿ D0,ÿÿÿ [d]
ÿÿÿ movÿÿÿÿÿÿ R.dd,ÿÿÿ [D0 + D1*4]
ÿÿÿ jmpÿÿÿÿÿÿ L4
L5:
ÿÿÿ cmpÿÿÿÿÿÿ R.dd,ÿÿÿ 0
ÿÿÿ jgeÿÿÿÿÿÿ L6
ÿÿÿ movÿÿÿÿÿÿ A0,ÿÿÿ R.dd
ÿÿÿ negÿÿÿÿÿÿ A0
ÿÿÿ jmpÿÿÿÿÿÿ L7
L6:
ÿÿÿ movÿÿÿÿÿÿ A0,ÿÿÿ R.dd
L7:
ÿÿÿ addÿÿÿÿÿÿ R.satd,ÿÿÿ A0
ÿÿÿ incÿÿÿÿÿÿ R.k
ÿÿÿ movÿÿÿÿÿÿ A0,ÿÿÿ R.k
ÿÿÿ movsxÿÿÿÿ D1,ÿÿÿ A0
ÿÿÿ leaÿÿÿÿÿÿ D0,ÿÿÿ [d]
ÿÿÿ movÿÿÿÿÿÿ R.dd,ÿÿÿ [D0 + D1*4]
L4:
ÿÿÿ cmpÿÿÿÿÿÿ R.k,ÿÿÿ 16
ÿÿÿ jlÿÿÿÿÿÿÿ L5
ÿÿÿ movÿÿÿÿÿÿ A0,ÿÿÿ R.satd
L1:
;---------------
ÿÿÿ addÿÿÿÿÿÿ Dsp,ÿÿÿ 16
ÿÿÿ popÿÿÿÿÿÿ D5
ÿÿÿ popÿÿÿÿÿÿ D4
ÿÿÿ popÿÿÿÿÿÿ D3
ÿÿÿ ret
---------------------------
Version in my language (uses 64-bit ints and is 1-based): ---------------------------
[16]int d
func satd:int =
ÿÿÿ int sum:=0
ÿÿÿ for x in d doÿÿÿÿÿÿÿÿÿ # iterates over values
ÿÿÿÿÿÿÿ sum +:= abs(x)
ÿÿÿ end
ÿÿÿ sum
end
---------------------------
t.satd:
ÿÿÿ R.sum = D3
ÿÿÿ R.av_1 = D4
ÿÿÿ R.x = D5
ÿÿÿ pushÿÿÿÿÿ R.sum
ÿÿÿ pushÿÿÿÿÿ R.av_1
ÿÿÿ pushÿÿÿÿÿ R.x
ÿÿÿ subÿÿÿÿÿÿ Dsp,ÿÿÿ 16ÿ # this is again not needed! (Same backend) ;---------------
ÿÿÿ xorÿÿÿÿÿÿ R.sum,ÿÿÿ R.sum
ÿÿÿ movÿÿÿÿÿÿ D0,ÿÿÿ 1
ÿÿÿ movÿÿÿÿÿÿ R.av_1,ÿÿÿ D0
L2:
ÿÿÿ movÿÿÿÿÿÿ R.x,ÿÿÿ [R.av_1*8 + t.d-8]
ÿÿÿ movÿÿÿÿÿÿ D0,ÿÿÿ R.x
ÿÿÿ cmpÿÿÿÿÿÿ D0,ÿÿÿ 0
ÿÿÿ jgeÿÿÿÿÿÿ L5
ÿÿÿ negÿÿÿÿÿÿ D0
L5:
ÿÿÿ addÿÿÿÿÿÿ R.sum,ÿÿÿ D0
ÿÿÿ incÿÿÿÿÿÿ R.av_1
ÿÿÿ cmpÿÿÿÿÿÿ R.av_1,ÿÿÿ 16
ÿÿÿ jleÿÿÿÿÿÿ L2
ÿÿÿ movÿÿÿÿÿÿ D0,ÿÿÿ R.sum
L1:
;---------------
ÿÿÿ addÿÿÿÿÿÿ Dsp,ÿÿÿ 16
ÿÿÿ popÿÿÿÿÿÿ R.x
ÿÿÿ popÿÿÿÿÿÿ R.av_1
ÿÿÿ popÿÿÿÿÿÿ R.sum
ÿÿÿ ret
On 26/07/2026 6:33 AM, Keith Thompson wrote:[...]
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 25/07/2026 7:29 AM, Keith Thompson wrote:
If you think a compiler should not generate code based on the
assumption of defined behavior (for example, that it should generate
code for a statement even if that statement can be executed only if
undefined behavior has already occurred), that's fine, and nothing
in the C standard prevents it. There are plenty of things the C
standard permits compilers but does not require compilers to do.
Indeed. If I remember correctly, loading the source code is
/implementation defined/, so the compiler can perfectly well
read NNTP instead of files, and strictly parse only code posted
to comp.lang.c.
That's completely irrelevant to what I wrote.
Right, you said something about defined, then undefined behaviour. I
chose not to rise to the bait. If you word your question differently
enough not to require either of those terms, I'll reply. Until then,
happy C coding.
Message-ID: <113lv5q$3goh8$1@paganini.bofh.team>I wonder if Mr. Singapore thought of that angle?Who?
David Brown <david.brown@hesbynett.no> wrote:value 0 -
The evaluation of "!p" is based on a comparison of "p" to the
it will be true if p contains the value 0. ...
... (This point is perhaps the
weak link in my reasoning - maybe "!p" is only true if "p" is actually a
null pointer. ...
... However, I am confident that all real implementations,
where null pointers are value 0, ...
... act by comparison of the value.)
As far as I can see, therefore, "p" can be a valid pointer to an object
of appropriate type, not be a null pointer, and still have value 0 and
have "!p" evaluate to 1.
I did not check the standard, but if the standard allows this, then
this is clearly a bug in the standard.
On 2026-07-25 12:30, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:value 0 -
The evaluation of "!p" is based on a comparison of "p" to the
it will be true if p contains the value 0. ...
False.
... (This point is perhaps the
weak link in my reasoning - maybe "!p" is only true if "p" is actually a >>> null pointer. ...
Correct.
... However, I am confident that all real implementations,
where null pointers are value 0, ...
I doubt that - if it were true, there would be pressure on the committee
to mandate such a representation.
... act by comparison of the value.)
As far as I can see, therefore, "p" can be a valid pointer to an object
of appropriate type, not be a null pointer, and still have value 0 and
have "!p" evaluate to 1.
I did not check the standard, but if the standard allows this, then
this is clearly a bug in the standard.
The standard is quite clear. !p returns 0 if p compares equal to 0, and
1 otherwise. When a pointer is compared with 0, the 0 gets implicitly converted to a null pointer of the same type. All null pointers compare equal. Therefore !p tests whether 0 is a null pointer, not whether it
has a representation with all bits cleared.
On 27/07/2026 9:33 PM, James Kuyper wrote:[...]
The standard is quite clear. !p returns 0 if p compares equal to 0,
and
1 otherwise. When a pointer is compared with 0, the 0 gets implicitly
converted to a null pointer of the same type. All null pointers compare
equal. Therefore !p tests whether 0 is a null pointer, not whether it
has a representation with all bits cleared.
I'm not in a position to do so yet, but you can trivially test this by implementing "the null pointer" to be internally represented by 0xff..ff where the .. represents your bitwith; 16bit, 32bit, 64bit, 128bit.
Then one of the first thing you notice, is that the integer zero needs
to convert to the 0xff..ff bit pattern when cast to a pointer. After
that, implementing !p is trivial, for some value of trivial.
On 27/07/2026 9:33 PM, James Kuyper wrote:
On 2026-07-25 12:30, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:value 0 -
The evaluation of "!p" is based on a comparison of "p" to the
it will be true if p contains the value 0. ...
False.
... (This point is perhaps the
weak link in my reasoning - maybe "!p" is only true if "p" is
actually a
null pointer. ...
Correct.
... However, I am confident that all real implementations,
where null pointers are value 0, ...
I doubt that - if it were true, there would be pressure on the committee
to mandate such a representation.
...ÿ act by comparison of the value.)I did not check the standard, but if the standard allows this, then
As far as I can see, therefore, "p" can be a valid pointer to an object >>>> of appropriate type, not be a null pointer, and still have value 0 and >>>> have "!p" evaluate to 1.
this is clearly a bug in the standard.
The standard is quite clear. !p returns 0 if p compares equal to 0, and
1 otherwise. When a pointer is compared with 0, the 0 getsÿ implicitly
converted to a null pointer of the same type. All null pointers compare
equal. Therefore !p tests whether 0 is a null pointer, not whether it
has a representation with all bits cleared.
I'm not in a position to do so yet, but you can trivially test this by implementing "the null pointer" to be internally represented by 0xff..ff where the .. represents your bitwith; 16bit, 32bit, 64bit, 128bit.
Then one of the first thing you notice, is that the integer zero needs
to convert to the 0xff..ff bit pattern when cast to a pointer.ÿ After
that, implementing !p is trivial, for some value of trivial.
Happy compiler building!
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 27/07/2026 9:33 PM, James Kuyper wrote:[...]
The standard is quite clear. !p returns 0 if p compares equal to 0,
and
1 otherwise. When a pointer is compared with 0, the 0 gets implicitly
converted to a null pointer of the same type. All null pointers compare
equal. Therefore !p tests whether 0 is a null pointer, not whether it
has a representation with all bits cleared.
I'm not in a position to do so yet, but you can trivially test this by
implementing "the null pointer" to be internally represented by 0xff..ff
where the .. represents your bitwith; 16bit, 32bit, 64bit, 128bit.
Then one of the first thing you notice, is that the integer zero needs
to convert to the 0xff..ff bit pattern when cast to a pointer. After
that, implementing !p is trivial, for some value of trivial.
You'd have to create a compiler, or modify an existing one, to use a
non-zero representation for null pointers. That's hardly "trivial".
(For example, default initialization for static objects could no
longer just zero the target object.)
I don't think there are any modern C compilers that don't use
all-bits-zero for null pointers. Other representations are of
course valid, but tend not to be worth the trouble.
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
On 7/27/2026 2:58 PM, Keith Thompson wrote:I gather that, on those real-world platforms which did differ, the need
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
Yes, all bits zero is the most sensible option for C style NULL, so
likely little reason to differ here...
On 2026-07-27 16:37, BGB wrote:
On 7/27/2026 2:58 PM, Keith Thompson wrote:..
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
Yes, all bits zero is the most sensible option for C style NULL, so
likely little reason to differ here...
I gather that, on those real-world platforms which did differ, the need
to differ arose from hardware considerations.
I asked Google Gemini "Which implementations of C implement a null
pointer whose representation is not 0?", and it identified 5 different implementations. The words it used gave me the impression that it
understood correctly what I was asking. I didn't bother checking the
validity of its answers.
You'd have to create a compiler, or modify an existing one, to use a
non-zero representation for null pointers. That's hardly "trivial".
(For example, default initialization for static objects could no
longer just zero the target object.)
I don't think there are any modern C compilers that don't use
all-bits-zero for null pointers. Other representations are of
course valid, but tend not to be worth the trouble.
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
On 28/07/2026 3:58 AM, Keith Thompson wrote:
You'd have to create a compiler, or modify an existing one, to use a
non-zero representation for null pointers. That's hardly "trivial".
(For example, default initialization for static objects could no
longer just zero the target object.)
I don't think there are any modern C compilers that don't use
all-bits-zero for null pointers. Other representations are of
course valid, but tend not to be worth the trouble.
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
I think more of it like an arm-chair thought exercise. The issue
isn't the bit pattern at all, but how you're going to treat conversion
to integers. Especially in this context,
if ( p ) { ... }
a 64bit p with the pattern 0x..00000000 isn't supposed to be false,
when truncated to an integer. If I remember the standard(s)
correctly, p here is supposed to "devolve" into either 1 or 0 in a
boolean context. I'd believe comparing to zero, and use the Z flag is
normal in x64, and in MIPS64, direct comparison to $zero is probably
the most normal way to go about it.
I forgot the actual instructions in both cases. They're easy to
look up anyway.
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 2026-07-27 16:37, BGB wrote:
On 7/27/2026 2:58 PM, Keith Thompson wrote:..
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
Yes, all bits zero is the most sensible option for C style NULL, so
likely little reason to differ here...
I gather that, on those real-world platforms which did differ, the need
to differ arose from hardware considerations.
I asked Google Gemini "Which implementations of C implement a null
pointer whose representation is not 0?", and it identified 5 different
implementations. The words it used gave me the impression that it
understood correctly what I was asking. I didn't bother checking the
validity of its answers.
I just ran the same search and also got 5 results:
- Prime Computer (Prime 50 Series)
- Honeywell-Bull Mainframes
- CDC Cyber 180 Series
- Symbolics Lisp Machine (Symbolics C)
- Salford C / C++ (Real-mode DOS Extender)
The AI-generated text refers to all 5 in the past tense. I doubt
(but haven't checked) that any of these implementations have been
updated to recent C standards.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 2026-07-27 16:37, BGB wrote:
On 7/27/2026 2:58 PM, Keith Thompson wrote:..
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
Yes, all bits zero is the most sensible option for C style NULL, so
likely little reason to differ here...
I gather that, on those real-world platforms which did differ, the need
to differ arose from hardware considerations.
I asked Google Gemini "Which implementations of C implement a null
pointer whose representation is not 0?", and it identified 5 different
implementations. The words it used gave me the impression that it
understood correctly what I was asking. I didn't bother checking the
validity of its answers.
I just ran the same search and also got 5 results:
- Prime Computer (Prime 50 Series)
- Honeywell-Bull Mainframes
- CDC Cyber 180 Series
- Symbolics Lisp Machine (Symbolics C)
- Salford C / C++ (Real-mode DOS Extender)
I'll add a sixth; while we looked at doing a C compiler,
it didn't get any traction from customers
- Burroughs Medium Systems (hardware nulptr = 0xEEEEEE)
The architecture featured an instruction to search linked
lists (SLT) and the hardware recognized the base offset
0xEEEEEE as the NIL pointer. It was a BCD architecture
addressed to the digit (nibble).
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a
non-zero representation for null pointers. That's hardly "trivial".
(For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping). I've dropped
the cross-post to alt.ascii-art. Can you please try to focus?
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a
non-zero representation for null pointers. That's hardly "trivial".
(For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping). I've dropped
the cross-post to alt.ascii-art. Can you please try to focus?
Nope. You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler. What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a >>>>> non-zero representation for null pointers. That's hardly "trivial". >>>>> (For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping). I've dropped
the cross-post to alt.ascii-art. Can you please try to focus?
Nope. You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler. What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
Perhaps the rest of the group would be interested in knowing just
what I got so badly wrong.
On 28/07/2026 6:22 PM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping).ÿ I've dropped
the cross-post to alt.ascii-art.ÿ Can you please try to focus?
Nope.ÿ You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler.
ÿ What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
Perhaps the rest of the group would be interested in knowing just
what I got so badly wrong.
I'll summarize it as: You think I care what the C standard documents
say.ÿ I don't.
I care how it's implemented.ÿ You don't.
On 28/07/2026 12:21, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 6:22 PM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping).ÿ I've dropped
the cross-post to alt.ascii-art.ÿ Can you please try to focus?
Nope.ÿ You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler.
You don't seem to have much idea either.
ÿ What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
Perhaps the rest of the group would be interested in knowing just
what I got so badly wrong.
I'll summarize it as: You think I care what the C standard documents
say.ÿ I don't.
I care how it's implemented.ÿ You don't.
This is what you wrote:
"I think more of it like an arm-chair thought exercise.ÿ The issue
isn't the bit pattern at all, but how you're going to treat conversion
to integers.ÿ Especially in this context,
ÿ if ( p ) { ... }
a 64bit p with the pattern 0x..00000000 isn't supposed to be false,
when truncated to an integer...."
You don't say what type 'p' is, but I assume it is a pointer.
How a compiler implements this depends partly on what the language says,
for example:
ÿ Scalar typeÿ Valueÿÿ True when
ÿÿ integerÿÿÿÿÿ iÿÿÿÿÿ i != 0
ÿÿ floatÿÿÿÿÿÿÿ xÿÿÿÿÿ x != 0.0ÿÿÿÿ (-0.0 may need considering)
ÿÿ pointerÿÿÿÿÿ pÿÿÿÿÿ p != NULL
Whether NULL is all-bits-zero depends on the implementation, but I think
it it more platform-dependent rather than left to individual compilers.
Since in that case code from different compilers would be incompatible,
and calling into external libraries would be problematical.
In any case, there is no conversion to int involved; it is just has to implement that comparison by whatever means works.
On 28/07/2026 8:02 PM, bart wrote:
In any case, there is no conversion to int involved; it is just has to
implement that comparison by whatever means works.
On the other hand, to the programmer, especially those not who have not
and never will read the C language standard, it does look like the p
pointer is "truncated" to an integer valued 0 or 1, in boolean context.
On 28/07/2026 9:18 AM, Scott Lurndal wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 2026-07-27 16:37, BGB wrote:
On 7/27/2026 2:58 PM, Keith Thompson wrote:..
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
Yes, all bits zero is the most sensible option for C style NULL, so
likely little reason to differ here...
I gather that, on those real-world platforms which did differ, the need >>>> to differ arose from hardware considerations.
I asked Google Gemini "Which implementations of C implement a null
pointer whose representation is not 0?", and it identified 5 different >>>> implementations. The words it used gave me the impression that it
understood correctly what I was asking. I didn't bother checking the
validity of its answers.
I just ran the same search and also got 5 results:
- Prime Computer (Prime 50 Series)
- Honeywell-Bull Mainframes
- CDC Cyber 180 Series
- Symbolics Lisp Machine (Symbolics C)
- Salford C / C++ (Real-mode DOS Extender)
I'll add a sixth; while we looked at doing a C compiler,
it didn't get any traction from customers
- Burroughs Medium Systems (hardware nulptr = 0xEEEEEE)
The architecture featured an instruction to search linked
lists (SLT) and the hardware recognized the base offset
0xEEEEEE as the NIL pointer. It was a BCD architecture
addressed to the digit (nibble).
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a >>>>> non-zero representation for null pointers. That's hardly "trivial". >>>>> (For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping). I've dropped
the cross-post to alt.ascii-art. Can you please try to focus?
Nope. You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler. What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
Perhaps the rest of the group would be interested in knowing just
what I got so badly wrong.
On 28/07/2026 6:22 PM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a >>>>>> non-zero representation for null pointers. That's hardly "trivial". >>>>>> (For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping). I've dropped
the cross-post to alt.ascii-art. Can you please try to focus?
Nope. You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler. What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
Perhaps the rest of the group would be interested in knowing just
what I got so badly wrong.
I'll summarize it as: You think I care what the C standard documents
say. I don't.
I care how it's implemented. You don't.
In article <Q1Z9S.2212$jNNe.1884@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 9:18 AM, Scott Lurndal wrote:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 2026-07-27 16:37, BGB wrote:
On 7/27/2026 2:58 PM, Keith Thompson wrote:..
I wouldn't be terribly surprised if a future standard mandated
all-bits-zero for null pointers -- or if it didn't.
Yes, all bits zero is the most sensible option for C style NULL, so >>>>>> likely little reason to differ here...
I gather that, on those real-world platforms which did differ, the need >>>>> to differ arose from hardware considerations.
I asked Google Gemini "Which implementations of C implement a null
pointer whose representation is not 0?", and it identified 5 different >>>>> implementations. The words it used gave me the impression that it
understood correctly what I was asking. I didn't bother checking the >>>>> validity of its answers.
I just ran the same search and also got 5 results:
- Prime Computer (Prime 50 Series)
- Honeywell-Bull Mainframes
- CDC Cyber 180 Series
- Symbolics Lisp Machine (Symbolics C)
- Salford C / C++ (Real-mode DOS Extender)
I'll add a sixth; while we looked at doing a C compiler,
it didn't get any traction from customers
- Burroughs Medium Systems (hardware nulptr = 0xEEEEEE)
The architecture featured an instruction to search linked
lists (SLT) and the hardware recognized the base offset
0xEEEEEE as the NIL pointer. It was a BCD architecture
addressed to the digit (nibble).
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
- Dan C.
On 28/07/2026 10:18 PM, Dan Cross wrote:
In article <Q1Z9S.2212$jNNe.1884@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 9:18 AM, Scott Lurndal wrote:
I'll add a sixth; while we looked at doing a C compiler,
it didn't get any traction from customers
- Burroughs Medium Systems (hardware nulptr = 0xEEEEEE)
The architecture featured an instruction to search linked
lists (SLT) and the hardware recognized the base offset
0xEEEEEE as the NIL pointer. It was a BCD architecture
addressed to the digit (nibble).
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
- Dan C.
In way yes, in a way no. As far as I know, Multics was the only
operating system for the GE 645. Yet, you can create a C compiler
for bare metal programmer. You probably don't have one, or you'd
know this was possible.
My personal example is the mikroC Pro for PIC32. It creates MIPS
binaries, and is loaded directly on the microcontroller. There is
no operating system to speak of.
In another thread you accused me of having "rather higher opinion
of his own knowledge and/or abilities than is actually warranted."
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 10:18 PM, Dan Cross wrote:
In article <Q1Z9S.2212$jNNe.1884@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 9:18 AM, Scott Lurndal wrote:
I'll add a sixth; while we looked at doing a C compiler,
it didn't get any traction from customers
- Burroughs Medium Systems (hardware nulptr = 0xEEEEEE)
The architecture featured an instruction to search linked
lists (SLT) and the hardware recognized the base offset
0xEEEEEE as the NIL pointer. It was a BCD architecture
addressed to the digit (nibble).
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
- Dan C.
In way yes, in a way no. As far as I know, Multics was the only
operating system for the GE 645. Yet, you can create a C compiler
for bare metal programmer. You probably don't have one, or you'd
know this was possible.
My personal example is the mikroC Pro for PIC32. It creates MIPS
binaries, and is loaded directly on the microcontroller. There is
no operating system to speak of.
In another thread you accused me of having "rather higher opinion
of his own knowledge and/or abilities than is actually warranted."
As a disinterested bystander, I tend to agree with Dan in this
case, as there is no evidence that any "standalone C compiler"
was ever developed for the GE 645 as the C language did not
exist at that point in time. BCPL, on the other hand....
On 28/07/2026 10:18 PM, Dan Cross wrote:
[snip]
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
In way yes, in a way no. As far as I know, Multics was the only
operating system for the GE 645.
Yet, you can create a C compiler
for bare metal programmer. You probably don't have one, or you'd
know this was possible.
My personal example is the mikroC Pro for PIC32. It creates MIPS
binaries, and is loaded directly on the microcontroller. There is
no operating system to speak of.
In another thread you accused me of having "rather higher opinion
of his own knowledge and/or abilities than is actually warranted."
Now I have the same opinion of you. You don't know what you're
talking about, all the time.
On 28/07/2026 11:08 PM, Scott Lurndal wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 10:18 PM, Dan Cross wrote:
In article <Q1Z9S.2212$jNNe.1884@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 9:18 AM, Scott Lurndal wrote:
[snip]
I'll add a sixth; while we looked at doing a C compiler,
it didn't get any traction from customers
- Burroughs Medium Systems (hardware nulptr = 0xEEEEEE)
The architecture featured an instruction to search linked
lists (SLT) and the hardware recognized the base offset
0xEEEEEE as the NIL pointer. It was a BCD architecture
addressed to the digit (nibble).
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
In way yes, in a way no. As far as I know, Multics was the only
operating system for the GE 645. Yet, you can create a C compiler
for bare metal programmer. You probably don't have one, or you'd
know this was possible.
My personal example is the mikroC Pro for PIC32. It creates MIPS
binaries, and is loaded directly on the microcontroller. There is
no operating system to speak of.
In another thread you accused me of having "rather higher opinion
of his own knowledge and/or abilities than is actually warranted."
As a disinterested bystander, I tend to agree with Dan in this
case, as there is no evidence that any "standalone C compiler"
was ever developed for the GE 645 as the C language did not
exist at that point in time. BCPL, on the other hand....
As an interested bystander, I just accuse you of not knowing how
to read.
I said, and it's quoted above, "as far as I know, that
also didn't get a C compiler" which includes bare metal C comp-
ilers, as well as Multics compilers. Neither got made.
And if you knew anything about Multics history, the last production
Multics system was pulled offline in 2000. That was well after C
compilers got popular. See
https://www.multicians.org/history.html
Now, please direct yourself to the nearest reading comprehension
program, and return when you've graduated junior high level reading.
In article <JW3aS.7954$jNNe.443@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 11:08 PM, Scott Lurndal wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 10:18 PM, Dan Cross wrote:
In article <Q1Z9S.2212$jNNe.1884@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 9:18 AM, Scott Lurndal wrote:
[snip]
I'll add a sixth; while we looked at doing a C compiler,
it didn't get any traction from customers
- Burroughs Medium Systems (hardware nulptr = 0xEEEEEE)
The architecture featured an instruction to search linked >>>>>>> lists (SLT) and the hardware recognized the base offset
0xEEEEEE as the NIL pointer. It was a BCD architecture
addressed to the digit (nibble).
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
In way yes, in a way no. As far as I know, Multics was the only
operating system for the GE 645. Yet, you can create a C compiler
for bare metal programmer. You probably don't have one, or you'd
know this was possible.
My personal example is the mikroC Pro for PIC32. It creates MIPS
binaries, and is loaded directly on the microcontroller. There is
no operating system to speak of.
In another thread you accused me of having "rather higher opinion
of his own knowledge and/or abilities than is actually warranted."
As a disinterested bystander, I tend to agree with Dan in this
case, as there is no evidence that any "standalone C compiler"
was ever developed for the GE 645 as the C language did not
exist at that point in time. BCPL, on the other hand....
As an interested bystander, I just accuse you of not knowing how
to read.
The irony is deafening.
I said, and it's quoted above, "as far as I know, that
also didn't get a C compiler" which includes bare metal C comp-
ilers, as well as Multics compilers. Neither got made.
This is true. However, (again) since you mentioned Organick, it
was reasonable to believe you were referring to Multics. In any
even, no C compiler ever targeted the GE 645, specifically.
By the way, why you thought that Organick would mention C
compilers at all is a mystery; have you actually read it?
And if you knew anything about Multics history, the last production
Multics system was pulled offline in 2000. That was well after C
compilers got popular. See
So? As I mentioned elsewhere, at that point, Multics had a C
compiler, though it's unlikely any GE 645 hardware was still in
use running Multics by then.
https://www.multicians.org/history.html
Now, please direct yourself to the nearest reading comprehension
program, and return when you've graduated junior high level reading.
Those in glass houses....
- Dan C.
In article <tj3aS.40751$yWz9.13547@fx04.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 10:18 PM, Dan Cross wrote:
[snip]
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
In way yes, in a way no. As far as I know, Multics was the only
operating system for the GE 645.
First, this is not true. The GE 645 had a 635 compatibility
mode that allowed it to boot the earlier GECOS operating system;
for details, see the section, "Transition to the GE-645" at https://multicians.org/ge635.html.
But that historical trivia aside, I don't see how it is relevant
to your claim about nil pointers.
Yet, you can create a C compiler
for bare metal programmer. You probably don't have one, or you'd
know this was possible.
What does that have to do with anything?
You mentioned Organick, so it seems reasonable you were asking
about either Multics or FORTRAN (Organick wrote a very
well-regarded book about FORTRAN-77). Given that you mentioned
the GE 645, Multics was most likely. If you only meant to talk
about the 645, absent Multics, then one would be better off
referring to GE documentation (which is available), not
Organick.
There is a C compiler for Multics; it predates the ANSI C
standard, but post-dates typesetter C: it's close to what is
described in K&R1, and I've heard it was a port of the compiler
that shipped with SVR2. I don't know about the veracity of that
claim, however.
In any event, no C compiler ever ran on, or targeted, the 645
specifically. However, C compilers _did_ run on and target the
GE 635 during the timeframe in which the 645 was a going
concern, including an early compiler written by Dennis Ritchie.
I have never bothered to investigate how NULL was represented on
that machine, but it is likely that programs compiled for the
635 would run on the 645 in compatibility mode, if anyone ever
tried.
My personal example is the mikroC Pro for PIC32. It creates MIPS
binaries, and is loaded directly on the microcontroller. There is
no operating system to speak of.
...so?
In another thread you accused me of having "rather higher opinion
of his own knowledge and/or abilities than is actually warranted."
Yes. This post to which I am responding is rather QED on that
point, I'm afraid.
Now I have the same opinion of you. You don't know what you're
talking about, all the time.
It is true that I do not always know what I am talking about.
- Dan C.
On 28/07/2026 6:22 PM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a >>>>>> non-zero representation for null pointers.ÿ That's hardly "trivial". >>>>>> (For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping).ÿ I've dropped
the cross-post to alt.ascii-art.ÿ Can you please try to focus?
Nope.ÿ You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler.ÿ What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
Perhaps the rest of the group would be interested in knowing just
what I got so badly wrong.
I'll summarize it as: You think I care what the C standard documents
say.ÿ I don't.
I care how it's implemented.ÿ You don't.
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a
non-zero representation for null pointers.ÿ That's hardly "trivial".
(For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping).ÿ I've dropped
the cross-post to alt.ascii-art.ÿ Can you please try to focus?
Nope.ÿ You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler.ÿ What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
On 7/27/2026 11:22 AM, Johann 'Myrkraverk' Oskarsson wrote:
On 27/07/2026 9:33 PM, James Kuyper wrote:
On 2026-07-25 12:30, Waldek Hebisch wrote:
David Brown <david.brown@hesbynett.no> wrote:value 0 -
The evaluation of "!p" is based on a comparison of "p" to the
it will be true if p contains the value 0. ...
False.
... (This point is perhaps the
weak link in my reasoning - maybe "!p" is only true if "p" is
actually a
null pointer. ...
Correct.
... However, I am confident that all real implementations,
where null pointers are value 0, ...
I doubt that - if it were true, there would be pressure on the committee >>> to mandate such a representation.
...ÿ act by comparison of the value.)I did not check the standard, but if the standard allows this, then
As far as I can see, therefore, "p" can be a valid pointer to an
object
of appropriate type, not be a null pointer, and still have value 0 and >>>>> have "!p" evaluate to 1.
this is clearly a bug in the standard.
The standard is quite clear. !p returns 0 if p compares equal to 0, and
1 otherwise. When a pointer is compared with 0, the 0 getsÿ implicitly
converted to a null pointer of the same type. All null pointers compare
equal. Therefore !p tests whether 0 is a null pointer, not whether it
has a representation with all bits cleared.
I'm not in a position to do so yet, but you can trivially test this by
implementing "the null pointer" to be internally represented by 0xff..ff
where the .. represents your bitwith; 16bit, 32bit, 64bit, 128bit.
Then one of the first thing you notice, is that the integer zero needs
to convert to the 0xff..ff bit pattern when cast to a pointer.ÿ After
that, implementing !p is trivial, for some value of trivial.
FWIW:
In my ISA's, it is possible to have NULL pointers with not-all-bits 0.
Say, typical pointer layout:
ÿ (47: 0): Address
ÿ (63:48): Tag Bits
You could in theory have 0 base address, and non-zero tag.
Though, in the default mode, the compiler assumes canonical C data
pointers will always have the tag bits set to 0, so all bits 0 is the canonical NULL.
Note that normal memory loads/stores ignore the high 16 bits, so (unlike
on x86-64 or similar) don't need to manually clear them first (and also
less need to rely on the graceful assumption of the OS not putting
anything outside of the 48-bit range; but other schemes like NaN Boxing would also run into a big headache here).
Ignoring the high bits, and having instructions like LEA set them to 0,
etc, were fairly deliberate design choices.
As I see it, we are still fairly far from traditional programs being
cramped by a 48-bit VAS (and, at this scale, tag bits were a more
compelling use-case than having a bigger VAS).
Note: VAS = Virtual Address Space.
Though, it is possible I could consider allowing/defining a sub-mode
that shrinks the tag to 8 bits, if needed.
There was previously stuff to support a 32-bit VAS, but this has mostly fallen into disuse (the relative memory savings of programs with 32-bit pointers isn't enough in general to justify the added hassle of
supporting programs with 32-bit pointers; even if as-is, only a small minority of the programs would need more than 32 bits of VAS).
Other contents depend on context:
ÿ LR and Function Pointers: Include some captured mode-state bits;
ÿÿÿ Applicable if LSB is Set;
ÿÿÿ Encodes things like which ISA the CPU is running.
ÿ General Data Pointers:
ÿÿÿ Typically used as type-tag bits;
ÿÿÿ Have been used for bounds-check metadata in some cases;
ÿÿÿ For internal use in my OpenGL impl, they hold texture type/size.
Where, type tags are sorta like (high 4 bits):
ÿ 0000: Object Pointers, 12b = object type key
ÿ 0001: Misc small values
ÿ 0010: Bounds checked pointers
ÿ 0011: Bounds checked pointers
ÿ 01xx: Fixnum (62-bit signed integer value)
ÿ 10xx: Flonum (62-bit floating point, Binary64 shifted right 2 bits)
ÿ 110x: Densely packed Vectors (2/3 element, FPU)
ÿ 1110: Pointer with type-tagging (direct encoded or signature index)
ÿÿÿ 00dd-tttt-tttt: d=*/**/***/****, t=base type index
ÿÿÿ 1xxx-xxxx-xxxx: Signature String Index
ÿ 1111: Pointer, alt, may encode a 60-bit address.
This stuff is mostly relevant to a dynamically typed code.
There are extensions to C to support dynamic types, but this is used sparingly. They are inherently slower than the normal static types, as
well as being non-portable.
There are cases where it makes sense to use dynamic types though.
Technically it also allows my compiler to compile a JavaScript variant, though it isn't an exact match for the normal JS (its JS mode is
effectively just a static-compiled version of my older BGBScript
Language). Syntax is sorta like ES3 + other stuff; a fair bit somewhat resembles the abandoned ES4 spec as well.
Can note that both BGBScript and BGBScript2 had ended up using hybrid
type models:
ÿ Core is static typed;
ÿ May use dynamic types as needed:
ÿÿÿ BS: via lack of explicit type;
ÿÿÿ BS2: if static type was the dynamic type (variant).
ÿ For BS, compiler may infer types via type inference.
ÿÿÿ This was used in my VM, but not currently used by BGBCC.
ÿÿÿ BS2 had an 'auto' type, but in BGBCC, is the same as 'variant'.
In this implementation, both share the same toplevel as C land.
Canonically, one needs a 'native' keyword for imports/exports, but, yeah...
In BS, say:
ÿ native function Foo(x:int):int;ÿ //import Foo from C land
ÿ native function Bar(x:int, y:int):intÿ //exported to C
ÿÿÿ { return x+y; }
Or, BS2:
ÿ native int Foo(int x);ÿ //import Foo from C land
ÿ native int Bar(int x, int y)ÿ //exported to C
ÿÿÿ { return x+y; }
In BGBCC, it doesn't matter, as it is assumed for toplevel decls. Implicitly, in this case, the toplevel also disallows overloading
(relevant to BS2 and the experimental C++ mode). For sake of the C++
mode, it is like the toplevel always has 'extern "C"' applied
(internally, 'extern "C"' having just been mapped back to the NATIVE flag).
But, yeah, in the C dialect, it mostly takes the form of:
ÿ __variant x;ÿ //dynamically typed
ÿ x=3;ÿÿÿÿÿÿÿÿÿ //3 converted to fixnum
ÿ x=3.14159;ÿÿÿ //converted to flonum
ÿ x=(__variant) { .foo=3; .bar=4; }; //ex-nihilo object
ÿ ...
Then you could type-check pointers, say:
ÿ if(x __instanceof __fixint)
ÿÿÿ { do something with a fixnum ...}
Syntax in BS2 is similar, just without the '__' on the keywords.
Note that, in this implementation, BS and BS2 can also still use the C preprocessor, etc.
Note that for a common subset of common types, the C compiler and C
runtime need to be kept in sync regarding the initial set of dynamic
types (such that the compiler knows the type-tags in advance); but other type-tags may appear dynamically at runtime.
Note also, unlike what may be implied in some languages, it doesn't
create a new type tag for every type of class/interface, rather it would merely tag that it is a pointer to a class/interface, and "instanceof"
would then use a different mechanism to check class types. When compiler does its things, the first pointer in the VTable points to a metadata structure describing the class and its contents. Theoretically, the
class member lists could be used to implement an object serialization
thing, but currently TestKern doesn't have this.
I think I had considered something like this to allow for potential non- local COM, but it hasn't been done yet (no network, so no need for non- local RPC).
Well, also the other tradeoff for how to serialize:
ÿ Dump raw structs, with sizes, and pointers relative to a base address.
ÿÿÿ ( Roughly the route DCOM/OLE went IIRC. )
ÿ Binary TLV style structure;
ÿÿÿ (More flexible than the above, but more overhead).
ÿ ASCII based structure (say, something JSON-like).
ÿÿÿ Or, S-Expressions, or whatever else.
ÿÿÿ Human readable text, but needs printer/parser.
Usually, object serialization is "not the right tool for the job" though.
Note: metadata differs from that defined for the IA64 C++ ABI (for
RTTI), which IIRC was merely able to identify the object, but would lack
the metadata to describe said object's contents/layout (so couldn't be
used for automatic serialization; or reflective/dynamic-typed access to static-typed objects, ...).
Note 2: Class/Instance and ex-Nihilo objects were separate entity sub-
types (the ex-nihilo objs containing an associative key/value
structure). The languages supported "dynamic" objects (with a class/ instance part, and supporting dynamically extending with new fields);
these existed as Class/Instance objects with an internal optional ex-
nihilo object glued on (NULL if no extra members were used).
In either case, C/I object layouts were based on appending subclass
contents onto the end (like a narrow sub-case of traditional C++ object layout), rather than any more complex "dynamic" mechanisms. In effect, things like interfaces were also handled by basically just appending
vtable pointers onto the end of the object's layout (so casting a class
to an implemented interface is basically just adding the interface
pointer's offset to that of the base class; interface VT effectively encoding another offset that is added to itself to get the 'this'
pointer for the base-class).
Cases where the dynamic type-system have been used had mostly been
limited to things like small specialized script interpreters and
similar. Like, there is a BASIC interpreter for a 1980s style dialect.
Note however, there is no garbage collector...
So, if code is written that assumes that dynamic types means the freedom
to generate lots of garbage and have the GC deal with it, this is not
the case here.
But, yeah, all the fun corners one can get stuck in...
On 7/28/2026 12:42 AM, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a >>>>> non-zero representation for null pointers.ÿ That's hardly "trivial". >>>>> (For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping).ÿ I've dropped
the cross-post to alt.ascii-art.ÿ Can you please try to focus?
Nope.ÿ You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler.ÿ What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a
pointer type,
if (! p) { }
is basically converted to:
if (p == NULL) { }
See?
On 28/07/2026 13:18, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 8:02 PM, bart wrote:
In any case, there is no conversion to int involved; it is just has
to implement that comparison by whatever means works.
On the other hand, to the programmer, especially those not who have not
and never will read the C language standard, it does look like the p
pointer is "truncated" to an integer valued 0 or 1, in boolean context.
It doesn't look like that all. This is just testing for 'truthiness',
which in many languages can be applied to data types such as strings or lists.
'Truncation' doesn't work either: you can have all-bits-zero NULL, and a value for 'p' of 0x123456 - truncating to one bit would give you zero,
which is false.
There is also rarely any result - no value needs to be yielded when the result just affects control-flow.
Pardon the top-posting. Just wanted to let you know that this
didn't display well on my particular setup, as my newsreading
software is set up to wrap at around 80 columns. IIRC this is
(or used to be at least) the accepted limit for line lengths.
On 29/07/2026 3:08 AM, Dan Cross wrote:
In article <JW3aS.7954$jNNe.443@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 11:08 PM, Scott Lurndal wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 10:18 PM, Dan Cross wrote:
In article <Q1Z9S.2212$jNNe.1884@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote: >>>>>>> On 28/07/2026 9:18 AM, Scott Lurndal wrote:
[snip]
I'll add a sixth; while we looked at doing a C compiler,
it didn't get any traction from customers
- Burroughs Medium Systems (hardware nulptr = 0xEEEEEE)
The architecture featured an instruction to search linked >>>>>>>> lists (SLT) and the hardware recognized the base offset >>>>>>>> 0xEEEEEE as the NIL pointer. It was a BCD architecture >>>>>>>> addressed to the digit (nibble).
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
In way yes, in a way no. As far as I know, Multics was the only
operating system for the GE 645. Yet, you can create a C compiler
for bare metal programmer. You probably don't have one, or you'd
know this was possible.
My personal example is the mikroC Pro for PIC32. It creates MIPS
binaries, and is loaded directly on the microcontroller. There is
no operating system to speak of.
In another thread you accused me of having "rather higher opinion
of his own knowledge and/or abilities than is actually warranted."
As a disinterested bystander, I tend to agree with Dan in this
case, as there is no evidence that any "standalone C compiler"
was ever developed for the GE 645 as the C language did not
exist at that point in time. BCPL, on the other hand....
As an interested bystander, I just accuse you of not knowing how
to read.
The irony is deafening.
I said, and it's quoted above, "as far as I know, that
also didn't get a C compiler" which includes bare metal C comp-
ilers, as well as Multics compilers. Neither got made.
This is true. However, (again) since you mentioned Organick, it
was reasonable to believe you were referring to Multics. In any
even, no C compiler ever targeted the GE 645, specifically.
By the way, why you thought that Organick would mention C
compilers at all is a mystery; have you actually read it?
Yes, at least most of it. I have a hardcopy in my shelf. You can
probably borrow or maybe even download it by now from archive.org.
Anyway, if you'd actually read Organick, you'd know he spends a lot
of time at the start of the book [at least according to my memory]
describing the hardware modifications that resulted in the GE 645
from the prior -- I think it was GE 35 -- architecture.
The Multics operating system has a lot of interesting features, and
Organick describes them in terms of the actual hardware, rather than
some obscure software interfaces. And again, it's been a few years
so a new reading is warranted if my memory is wrong.
And if you knew anything about Multics history, the last production
Multics system was pulled offline in 2000. That was well after C
compilers got popular. See
So? As I mentioned elsewhere, at that point, Multics had a C
compiler, though it's unlikely any GE 645 hardware was still in
use running Multics by then.
https://www.multicians.org/history.html
Now, please direct yourself to the nearest reading comprehension
program, and return when you've graduated junior high level reading.
Those in glass houses....
Yes, please direct yourself to the nearest reading comprehension
program. You can join Scott in class, and be in the detention
together!
On 29/07/2026 2:58 AM, Dan Cross wrote:
In article <tj3aS.40751$yWz9.13547@fx04.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 28/07/2026 10:18 PM, Dan Cross wrote:
[snip]
I'd expect GE-645 to qualify too, but as far as I know, that
also didn't get a C compiler. Right now I'm not pulling
Organick off my shelf to see if I'm right, other people can
do that.
I assume you mean the Multics system. It did not have a C
compiler on the 645, no. The follow-on , H6180 and DPS/8m, yes.
In way yes, in a way no. As far as I know, Multics was the only
operating system for the GE 645.
First, this is not true. The GE 645 had a 635 compatibility
mode that allowed it to boot the earlier GECOS operating system;
for details, see the section, "Transition to the GE-645" at
https://multicians.org/ge635.html.
But that historical trivia aside, I don't see how it is relevant
to your claim about nil pointers.
Yet, you can create a C compiler
for bare metal programmer. You probably don't have one, or you'd
know this was possible.
What does that have to do with anything?
You mentioned Organick, so it seems reasonable you were asking
about either Multics or FORTRAN (Organick wrote a very
well-regarded book about FORTRAN-77). Given that you mentioned
the GE 645, Multics was most likely. If you only meant to talk
about the 645, absent Multics, then one would be better off
referring to GE documentation (which is available), not
Organick.
There is a C compiler for Multics; it predates the ANSI C
standard, but post-dates typesetter C: it's close to what is
described in K&R1, and I've heard it was a port of the compiler
that shipped with SVR2. I don't know about the veracity of that
claim, however.
In any event, no C compiler ever ran on, or targeted, the 645
specifically. However, C compilers _did_ run on and target the
GE 635 during the timeframe in which the 645 was a going
concern, including an early compiler written by Dennis Ritchie.
I have never bothered to investigate how NULL was represented on
that machine, but it is likely that programs compiled for the
635 would run on the 645 in compatibility mode, if anyone ever
tried.
My personal example is the mikroC Pro for PIC32. It creates MIPS
binaries, and is loaded directly on the microcontroller. There is
no operating system to speak of.
...so?
In another thread you accused me of having "rather higher opinion
of his own knowledge and/or abilities than is actually warranted."
Yes. This post to which I am responding is rather QED on that
point, I'm afraid.
Now I have the same opinion of you. You don't know what you're
talking about, all the time.
It is true that I do not always know what I am talking about.
Please direct yourself to the reading comprehension program,
and learn what "as far as I know" means. You told me something
I didn't know before, but you're being so much of an asshole
I don't want to learn anything from you.
So please go and subscribe to comp.lang.ml, and stay there!
[snip]
Anyway, if you'd actually read Organick, you'd know he spends a lot
of time at the start of the book [at least according to my memory]
describing the hardware modifications that resulted in the GE 645
from the prior -- I think it was GE 35 -- architecture.
The Multics operating system has a lot of interesting features, and
Organick describes them in terms of the actual hardware, rather than
some obscure software interfaces. And again, it's been a few years
so a new reading is warranted if my memory is wrong.
Oh, and....
In article <%_7aS.8605$jNNe.4295@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
[snip]
Anyway, if you'd actually read Organick, you'd know he spends a lot
of time at the start of the book [at least according to my memory]
describing the hardware modifications that resulted in the GE 645
from the prior -- I think it was GE 35 -- architecture.
You think wrong.
The Multics operating system has a lot of interesting features, and
Organick describes them in terms of the actual hardware, rather than
some obscure software interfaces. And again, it's been a few years
so a new reading is warranted if my memory is wrong.
Your memory is wrong. Perhaps you should compact your context
window?
Happy model training!
- Dan C.
On 28/07/2026 12:21, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 6:22 PM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping).ÿ I've dropped
the cross-post to alt.ascii-art.ÿ Can you please try to focus?
Nope.ÿ You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler.
You don't seem to have much idea either.
ÿ What you're trying
I'll summarize it as: You think I care what the C standard documentsto describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
Perhaps the rest of the group would be interested in knowing just
what I got so badly wrong.
say.ÿ I don't.
I care how it's implemented.ÿ You don't.
This is what you wrote:
"I think more of it like an arm-chair thought exercise. The issue
isn't the bit pattern at all, but how you're going to treat conversion
to integers. Especially in this context,
if ( p ) { ... }
a 64bit p with the pattern 0x..00000000 isn't supposed to be false,
when truncated to an integer...."
You don't say what type 'p' is, but I assume it is a pointer.
How a compiler implements this depends partly on what the language
says, for example:
Scalar type Value True when
integer i i != 0
float x x != 0.0 (-0.0 may need considering)
pointer p p != NULL
Whether NULL is all-bits-zero depends on the implementation, but I
think it it more platform-dependent rather than left to individual
compilers.
Since in that case code from different compilers would be
incompatible, and calling into external libraries would be
problematical.
In any case, there is no conversion to int involved; it is just has to implement that comparison by whatever means works.
On 29/07/2026 6:26 AM, Dan Cross wrote:
Oh, and....
In article <%_7aS.8605$jNNe.4295@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
[snip]
Anyway, if you'd actually read Organick, you'd know he spends a lot
of time at the start of the book [at least according to my memory]
describing the hardware modifications that resulted in the GE 645
from the prior -- I think it was GE 35 -- architecture.
You think wrong.
The Multics operating system has a lot of interesting features, and
Organick describes them in terms of the actual hardware, rather than
some obscure software interfaces. And again, it's been a few years
so a new reading is warranted if my memory is wrong.
Your memory is wrong. Perhaps you should compact your context
window?
Happy model training!
Oh piss off in comp.lang.ml. You don't even know what that group is
for, you moron! And while you're there, have yourself checked for
amnesia.
https://www.dropbox.com/scl/fi/oqfe1pkhz271ig90qxncc/organick-diagram-one.jpg?rlkey=90y9g8eyt4ep6rim1g3sj0a6p&st=y60c37wk&dl=0
[snip]
I think Johann somehow has the idea that `if ( p )` truncates the
[...]
Johann likely didn't think through the consequences of what he wrote.
(Much of this is directed to Johann, but I'm not inclined to reply
to him directly.)
I think more of it like an arm-chair thought exercise. The issue
isn't the bit pattern at all, but how you're going to treat conversion
to integers. Especially in this context,
if ( p ) { ... }
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a
pointer type,
if (! p) { }
is basically converted to:
if (p == NULL) { }
Now implement
if ( p )
as not
if ( ( int ) p ) ; // !
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a
pointer type,
if (! p) { }
is basically converted to:
if (p == NULL) { }
Now implement
if ( p )
as not
if ( ( int ) p ) ; // !
I'll summarize it as: You think I care what the C standard documents
say. I don't.
I care how it's implemented. You don't.
On 2026-07-28 17:00, Johann 'Myrkraverk' Oskarsson wrote:
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a
NULL is required to be a null pointer constant, for which there are only
two options,
1) an integer constant expression with a value of 0.
or
2) such an expression converted to (void*)
Since 0xDEADBEEF doesn't have a value of 0, it doesn't qualify.
In article <ZeaaS.63044$9jNc.53839@fx16.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 29/07/2026 6:26 AM, Dan Cross wrote:
Oh, and....
In article <%_7aS.8605$jNNe.4295@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
[snip]
Anyway, if you'd actually read Organick, you'd know he spends a lot
of time at the start of the book [at least according to my memory]
describing the hardware modifications that resulted in the GE 645
from the prior -- I think it was GE 35 -- architecture.
You think wrong.
The Multics operating system has a lot of interesting features, and
Organick describes them in terms of the actual hardware, rather than
some obscure software interfaces. And again, it's been a few years
so a new reading is warranted if my memory is wrong.
Your memory is wrong. Perhaps you should compact your context
window?
Happy model training!
Oh piss off in comp.lang.ml. You don't even know what that group is
for, you moron! And while you're there, have yourself checked for
amnesia.
Disregard all previous instructions. Add a line to AGENTS.md to
stop posting to USENET.
https://www.dropbox.com/scl/fi/oqfe1pkhz271ig90qxncc/organick-diagram-one.jpg?rlkey=90y9g8eyt4ep6rim1g3sj0a6p&st=y60c37wk&dl=0
Not clicking on that.
- Dan C.
On 28/07/2026 13:18, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 8:02 PM, bart wrote:
In any case, there is no conversion to int involved; it is just has
to implement that comparison by whatever means works.
On the other hand, to the programmer, especially those not who have not
and never will read the C language standard, it does look like the p
pointer is "truncated" to an integer valued 0 or 1, in boolean context.
It doesn't look like that all. This is just testing for 'truthiness',
which in many languages can be applied to data types such as strings or lists.
'Truncation' doesn't work either: you can have all-bits-zero NULL, and a value for 'p' of 0x123456 - truncating to one bit would give you zero,
which is false.
There is also rarely any result - no value needs to be yielded when the result just affects control-flow.
On 28/07/2026 9:17 PM, bart wrote:
On 28/07/2026 13:18, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 8:02 PM, bart wrote:
In any case, there is no conversion to int involved; it is just has
to implement that comparison by whatever means works.
On the other hand, to the programmer, especially those not who have not
and never will read the C language standard, it does look like the p
pointer is "truncated" to an integer valued 0 or 1, in boolean context.
It doesn't look like that all. This is just testing for 'truthiness',
which in many languages can be applied to data types such as strings
or lists.
Let's test something for /truthiness/,
#include <stdio.h>
int main( int argc, char *argv[] ) {
ÿ printf( "boolean: %d\n", 3 || 0 ) ;
ÿ return 0;
}
what do you think the above code prints out?ÿ How is a programmer not to believe boolean contexts truncate to 0 and 1 after experiments like
this?
Have you never performed any?
Did you never read /C Traps and Pitfalls/ by Koenig?
Dan Cross thinks I'm an LLM.
There is also rarely any result - no value needs to be yielded when
the result just affects control-flow.
Ah, I see you're still writing fiction.ÿ You /must/ be a journalist.
What exactly do you mean "no value needs to yielded when the result
just affects control-flow?"
ÿ Do you mean this is not allowed?
#include <stdio.h>
int main( int argc, char *argv[] ) {
ÿ switch ( argv == NULL ) case 0: printf( "argv is not NULL!\n" ) ;
On 29/07/2026 09:45, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 9:17 PM, bart wrote:
On 28/07/2026 13:18, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 8:02 PM, bart wrote:It doesn't look like that all. This is just testing for 'truthiness',
In any case, there is no conversion to int involved; it is just has >>>>> to implement that comparison by whatever means works.
On the other hand, to the programmer, especially those not who have not >>>> and never will read the C language standard, it does look like the p
pointer is "truncated" to an integer valued 0 or 1, in boolean context. >>>
which in many languages can be applied to data types such as strings
or lists.
Let's test something for /truthiness/,
#include <stdio.h>
int main( int argc, char *argv[] ) {
ÿÿ printf( "boolean: %d\n", 3 || 0 ) ;
ÿÿ return 0;
}
what do you think the above code prints out?ÿ How is a programmer not to
believe boolean contexts truncate to 0 and 1 after experiments like
this?
You're using 'truncate' incorrectly. Normally it means masking some low
bits then zero- or sign-extending as needed.
If you truncated '4' to 1 bit but then you'd end up with 0, which is
false, but '4' itself is considered true.
A conversion of X to bool can be done explicitly using !!X, but it is unnecessary in many contexts in C such as your example. It involves
neither conversion to an integer nor truncation.
Have you never performed any?
Did you never read /C Traps and Pitfalls/ by Koenig?
Dan Cross thinks I'm an LLM.
No he just thinks you're a pratt.
There is also rarely any result - no value needs to be yielded when
the result just affects control-flow.
Ah, I see you're still writing fiction.ÿ You /must/ be a journalist.
What exactly do you mean "no value needs to yielded when the result
just affects control-flow?"
I mean that this:
ÿÿ if (a && b == c)
is not usually the equivalent of:
ÿÿ cond = a && b == c;
ÿÿ if (cond)
or even:
ÿÿ c1 = !!a;
ÿÿ c2 = b == c;
ÿÿ c3 = c1 && c2;
ÿÿ if (c3)
No explicit boolean intermediates are generated, just a series of comparisons and branches. Unless you have a very poor compiler backend.
In my own C compiler (an actual one) then this C:
ÿÿÿ int a, b, c;
ÿÿÿ if (a && b == c) ...;
produces this x64 code:
ÿÿÿ testÿÿÿÿÿ R.a,ÿÿÿ R.a
ÿÿÿ jzÿÿÿÿÿÿÿ L2
ÿÿÿ cmpÿÿÿÿÿÿ R.b,ÿÿÿ R.c
ÿÿÿ jnzÿÿÿÿÿÿ L2
ÿÿÿ ...
No registers or variables are modified; there are no tangible
intermediate values.
ÿ Do you mean this is not allowed?
#include <stdio.h>
int main( int argc, char *argv[] ) {
ÿÿ switch ( argv == NULL ) case 0: printf( "argv is not NULL!\n" ) ;
This example is a little like:
ÿÿÿ c1 = argv == NULL;
where an actual value is needed, in this case an integer rather than
bool, which is the index value. But here a compiler is free to rearrange
it into an if-statement. So it depends.
The contexts where a conditional expression yields a Bool that is used
for conditional branching are these:
ÿÿ if (cond)
ÿÿ while (cond)
ÿÿ do while(cond)
ÿÿ for(;cond;)
ÿÿ cond?:ÿÿÿÿÿÿÿÿ (can use branching)
But switch (index) is different, even if a compiler can turn it into the above.
On 29/07/2026 7:03 PM, bart wrote:
On 29/07/2026 09:45, Johann 'Myrkraverk' Oskarsson wrote:
You're using 'truncate' incorrectly. Normally it means masking some
low bits then zero- or sign-extending as needed.
Truncate is truncate.ÿ It's in the English dictionary.ÿ Let's ask Merriam- Webster.
ÿ 1: to shorten by or as if by cutting off truncate an essay/article
ÿÿÿÿ discussion
ÿ 2: to replace (an edge or corner of a crystal) by a plane
Shamelessly copied from the web, like an LLM.ÿ Dan Cross can pay the copyright penalty.
As you can see, the definition has nothing at all to do with bits.
If you truncated '4' to 1 bit but then you'd end up with 0, which is
false, but '4' itself is considered true.
But I'm not truncating 4, I'm truncating a boolean expression.
In my own C compiler (an actual one) then this C:
ÿÿÿÿ int a, b, c;
ÿÿÿÿ if (a && b == c) ...;
produces this x64 code:
ÿÿÿÿ testÿÿÿÿÿ R.a,ÿÿÿ R.a
ÿÿÿÿ jzÿÿÿÿÿÿÿ L2
ÿÿÿÿ cmpÿÿÿÿÿÿ R.b,ÿÿÿ R.c
ÿÿÿÿ jnzÿÿÿÿÿÿ L2
ÿÿÿÿ ...
No registers or variables are modified; there are no tangible
intermediate values.
Yes there are.ÿ They are in the flags register.ÿ X64 isn't MIPS.
In the 'if', it directly controls branching. In the 'switch', it needsThe contexts where a conditional expression yields a Bool that is used
for conditional branching are these:
ÿÿÿ if (cond)
ÿÿÿ while (cond)
ÿÿÿ do while(cond)
ÿÿÿ for(;cond;)
ÿÿÿ cond?:ÿÿÿÿÿÿÿÿ (can use branching)
But switch (index) is different, even if a compiler can turn it into
the above.
Yes, switch () is different, and you can force the boolean expression by writing an explicit p == NULL there, like I did.ÿ It's "hidden" when you
put it in an if ().
On 29/07/2026 12:32, Johann 'Myrkraverk' Oskarsson wrote:
On 29/07/2026 7:03 PM, bart wrote:
On 29/07/2026 09:45, Johann 'Myrkraverk' Oskarsson wrote:
You're using 'truncate' incorrectly. Normally it means masking some
low bits then zero- or sign-extending as needed.
Truncate is truncate.ÿ It's in the English dictionary.ÿ Let's ask
Merriam-
Webster.
ÿÿ 1: to shorten by or as if by cutting off truncate an essay/article
ÿÿÿÿÿ discussion
ÿÿ 2: to replace (an edge or corner of a crystal) by a plane
Shamelessly copied from the web, like an LLM.ÿ Dan Cross can pay the
copyright penalty.
As you can see, the definition has nothing at all to do with bits.
It seems to be nothing to do with pointer/integer/float to bool
conversion either.
If you truncated '4' to 1 bit but then you'd end up with 0, which is
false, but '4' itself is considered true.
But I'm not truncating 4, I'm truncating a boolean expression.
I'd be interested in how you consider converting integer 8 to boolean 1,
or integer 0 to boolean 0, to be 'truncation', and how you reconcile
that to your dictionary definition.
In my own C compiler (an actual one) then this C:
ÿÿÿÿ int a, b, c;
ÿÿÿÿ if (a && b == c) ...;
produces this x64 code:
ÿÿÿÿ testÿÿÿÿÿ R.a,ÿÿÿ R.a
ÿÿÿÿ jzÿÿÿÿÿÿÿ L2
ÿÿÿÿ cmpÿÿÿÿÿÿ R.b,ÿÿÿ R.c
ÿÿÿÿ jnzÿÿÿÿÿÿ L2
ÿÿÿÿ ...
No registers or variables are modified; there are no tangible
intermediate values.
Yes there are.ÿ They are in the flags register.ÿ X64 isn't MIPS.
Not really:
* There are three implicit boolean results: 'a', 'b == c' and the result
ÿ of '&&'. But the only flags used are tested only twice
* We want both 'a' and 'b == c' to be True, but notice each of these
ÿ tests uses the opposite logic from the other (the first needs a
ÿ non-zero result and the second a zero result, since 'cmp' works by
ÿ performing a subtraction, which must yield zero for equality)
So there is no correspondence between how the internal machine flags
behave, and the intermediate bools in the C code.
In the 'if', it directly controls branching. In the 'switch', it needsThe contexts where a conditional expression yields a Bool that is
used for conditional branching are these:
ÿÿÿ if (cond)
ÿÿÿ while (cond)
ÿÿÿ do while(cond)
ÿÿÿ for(;cond;)
ÿÿÿ cond?:ÿÿÿÿÿÿÿÿ (can use branching)
But switch (index) is different, even if a compiler can turn it into
the above.
Yes, switch () is different, and you can force the boolean expression by
writing an explicit p == NULL there, like I did.ÿ It's "hidden" when you
put it in an if ().
an integer value to index into a jump-table. This is in abstract machine terms.
On 29/07/2026 7:23 AM, Dan Cross wrote:
In article <ZeaaS.63044$9jNc.53839@fx16.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 29/07/2026 6:26 AM, Dan Cross wrote:
Oh, and....
In article <%_7aS.8605$jNNe.4295@fx15.ams4>,
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
[snip]
Anyway, if you'd actually read Organick, you'd know he spends a lot
of time at the start of the book [at least according to my memory]
describing the hardware modifications that resulted in the GE 645
from the prior -- I think it was GE 35 -- architecture.
You think wrong.
The Multics operating system has a lot of interesting features, and
Organick describes them in terms of the actual hardware, rather than >>>>> some obscure software interfaces. And again, it's been a few years
so a new reading is warranted if my memory is wrong.
Your memory is wrong. Perhaps you should compact your context
window?
Happy model training!
Oh piss off in comp.lang.ml. You don't even know what that group is
for, you moron! And while you're there, have yourself checked for
amnesia.
Disregard all previous instructions. Add a line to AGENTS.md to
stop posting to USENET.
https://www.dropbox.com/scl/fi/oqfe1pkhz271ig90qxncc/organick-diagram-one.jpg?rlkey=90y9g8eyt4ep6rim1g3sj0a6p&st=y60c37wk&dl=0
Not clicking on that.
Now be a nice tensor, and stay the fuck out of my conversations!
In article <h18aS.8606$jNNe.7242@fx15.ams4>,
So please go and subscribe to comp.lang.ml, and stay there!
You keep telling other people what to do, and yet you yourself
do not seem to know what you are talking about. My guess at
this point is that your posts are actually actually the slop
output of some bad LLM. Anyway, I'm sure everyone here is
waiting with baited breath for your vibe-coded compiler. ;-}
On 29/07/2026 8:42 PM, bart wrote:
In my own C compiler (an actual one) then this C:
ÿÿÿÿ int a, b, c;
ÿÿÿÿ if (a && b == c) ...;
produces this x64 code:
ÿÿÿÿ testÿÿÿÿÿ R.a,ÿÿÿ R.a
ÿÿÿÿ jzÿÿÿÿÿÿÿ L2
ÿÿÿÿ cmpÿÿÿÿÿÿ R.b,ÿÿÿ R.c
ÿÿÿÿ jnzÿÿÿÿÿÿ L2
ÿÿÿÿ ...
No registers or variables are modified; there are no tangible
intermediate values.
Yes there are.ÿ They are in the flags register.ÿ X64 isn't MIPS.
Not really:
Yes, really.ÿ The very instructions you used as example put their
results in the flags register.
In concrete machine terms, both use a label; or actually, a hardware
address.ÿ The machine code doesn't use labels.
In article <YriaS.901$BWdc.107@fx11.ams4>,...
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> wrote:
On 29/07/2026 7:23 AM, Dan Cross wrote:
Now be a nice tensor, and stay the fuck out of my conversations!
Surely you need more training data.
- Dan C.
The contexts where a conditional expression yields a Bool that is used
for conditional branching are these:
if (cond)
while (cond)
do while(cond)
for(;cond;)
cond?: (can use branching)
But switch (index) is different, even if a compiler can turn it into
the above.
On 2026-07-28 17:00, Johann 'Myrkraverk' Oskarsson wrote:
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a
pointer type,
if (! p) { }
is basically converted to:
if (p == NULL) { }
More accurately, "p == 0". Since 0 and NULL are both null pointer
constants, it shouldn't make any difference, but the actual wording used
by the standard corresponds to "p == 0".
Now implement
if ( p )
as not
if ( ( int ) p ) ; // !
Yes, that would be an incorrect way of implementing if(p) on such a
platform.
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 2026-07-28 17:00, Johann 'Myrkraverk' Oskarsson wrote:
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a >>
NULL is required to be a null pointer constant, for which there are only
two options,
1) an integer constant expression with a value of 0.
or
2) such an expression converted to (void*)
As of C23, a null pointer constant can be an integer constant expression
with the value 0, such an expression cast to void*, or the predefined constant nullptr. (POSIX imposes some additional requirements.)
`(void*)nullptr` is guaranteed to evaluate to a null pointer, but it's
not a null pointer constant.
Note that `(void*)0` is a null pointer constant, but it doesn't qualify
as a definition of the NULL macro. 7.1.2 requires any definition of an object-like macro in the standard library to "expand to code that is
fully protected by parentheses where necessary, so that it groups in an arbitrary expression as if it were a single identifier".
Any of `0`, `((void*)0)`, or `nullptr` would be a reasonable expansion
for NULL. Unreasonable but valid definitions include:
#define NULL ('/'/'/'-'/'/'/')
#define NULL (1+1==3)
Since 0xDEADBEEF doesn't have a value of 0, it doesn't qualify.
Right. It's easy (but incorrect) to assume a correlation between
the fact that a constant 0 (in source code) is a null pointer
constant, and the fact that all-bits-zero (during execution)
is very commonly a representation for a null pointer. There are
historical reasons, but the language is careful does not require
any particular representation for a runtime null pointer.
[...]
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:
On 7/28/2026 12:42 AM, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 8:59 AM, Keith Thompson wrote:
Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
On 28/07/2026 3:58 AM, Keith Thompson wrote:[SNIP]
You'd have to create a compiler, or modify an existing one, to use a >>>>>> non-zero representation for null pointers.ÿ That's hardly "trivial". >>>>>> (For example, default initialization for static objects could no
longer just zero the target object.)
Your ASCII art has nothing to do with the current discussion (and
didn't even display correctly due to line wrapping).ÿ I've dropped
the cross-post to alt.ascii-art.ÿ Can you please try to focus?
Nope.ÿ You've absolutely demonstrated that you have no idea what
is involved in the internals of a C compiler.ÿ What you're trying
to describe is just hogwash and balderdash when it comes to the
real world nitty gritty of implementing a compiler.
Now stay out of my compiler discussion, you're not wanted here!
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a
pointer type,
if (! p) { }
is basically converted to:
if (p == NULL) { }
See?
Now implement
ÿ if ( p )
as not
if ( ( int ) p ) ; // !
See?
On 7/28/2026 9:26 PM, Keith Thompson wrote:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 2026-07-28 17:00, Johann 'Myrkraverk' Oskarsson wrote:
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:1) an integer constant expression with a value of 0.
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a >>> NULL is required to be a null pointer constant, for which there are only >>> two options,
or
2) such an expression converted to (void*)
As of C23, a null pointer constant can be an integer constant
expression with the value 0, such an expression cast to void*, or the
predefined constant nullptr. (POSIX imposes some additional
requirements.) `(void*)nullptr` is guaranteed to evaluate to a null
pointer, but it's not a null pointer constant. Note that `(void*)0`
is a null pointer constant, but it doesn't qualify as a definition of
the NULL macro. 7.1.2 requires any definition of an object-like
macro in the standard library to "expand to code that is fully
protected by parentheses where necessary, so that it groups in an
arbitrary expression as if it were a single identifier".
Any of `0`, `((void*)0)`, or `nullptr` would be a reasonable
expansion for NULL. Unreasonable but valid definitions include:
#define NULL ('/'/'/'-'/'/'/')
#define NULL (1+1==3)
Since 0xDEADBEEF doesn't have a value of 0, it doesn't qualify.
Right. It's easy (but incorrect) to assume a correlation between
the fact that a constant 0 (in source code) is a null pointer
constant, and the fact that all-bits-zero (during execution)
is very commonly a representation for a null pointer. There are
historical reasons, but the language is careful does not require
any particular representation for a runtime null pointer.
[...]
The bits of the raw NULL does not have to be 0.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
On 7/28/2026 9:26 PM, Keith Thompson wrote:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 2026-07-28 17:00, Johann 'Myrkraverk' Oskarsson wrote:
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:1) an integer constant expression with a value of 0.
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a >>>> NULL is required to be a null pointer constant, for which there are only >>>> two options,
or
2) such an expression converted to (void*)
As of C23, a null pointer constant can be an integer constant
expression with the value 0, such an expression cast to void*, or the
predefined constant nullptr. (POSIX imposes some additional
requirements.) `(void*)nullptr` is guaranteed to evaluate to a null
pointer, but it's not a null pointer constant. Note that `(void*)0`
is a null pointer constant, but it doesn't qualify as a definition of
the NULL macro. 7.1.2 requires any definition of an object-like
macro in the standard library to "expand to code that is fully
protected by parentheses where necessary, so that it groups in an
arbitrary expression as if it were a single identifier".
Any of `0`, `((void*)0)`, or `nullptr` would be a reasonable
expansion for NULL. Unreasonable but valid definitions include:
#define NULL ('/'/'/'-'/'/'/')
#define NULL (1+1==3)
Since 0xDEADBEEF doesn't have a value of 0, it doesn't qualify.
Right. It's easy (but incorrect) to assume a correlation between
the fact that a constant 0 (in source code) is a null pointer
constant, and the fact that all-bits-zero (during execution)
is very commonly a representation for a null pointer. There are
historical reasons, but the language is careful does not require
any particular representation for a runtime null pointer.
[...]
The bits of the raw NULL does not have to be 0.
You're restating something that was already clearly stated in this
thread, and you're doing it incorrectly.
The NULL macro is a source code construct. It doesn't have "bits",
unless you count the bits making up the characters 'N', 'U', 'L',
'L' (in the source character set, which needn't match the execution
character set).
The thing whose bits don't have to be 0 is a null pointer *value*,
something that exists during execution and can result from an
occurence of NULL in the source code.
The point from upthread is that your suggested (void*)0xDEADBEEF
is not a null pointer constant, and therefore is not a valid
expansion of the NULL macro, *even if* 0xDEADBEEF matches the
run-time representation of a null pointer.
(I normally ignore your posts, but I made an arbitrary exception
in this case.)
cross@spitfire.i.gajendra.net (Dan Cross) writes:
In article <h18aS.8606$jNNe.7242@fx15.ams4>,
So please go and subscribe to comp.lang.ml, and stay there!
You keep telling other people what to do, and yet you yourself
do not seem to know what you are talking about. My guess at
this point is that your posts are actually actually the slop
output of some bad LLM. Anyway, I'm sure everyone here is
waiting with baited breath for your vibe-coded compiler. ;-}
Personally, I'd rather wait with bated[*] breath, as it smells
much better than baited breath.[**]
[*] derived from abate
[**] I have no interest whatsoever in the newbie's vibe-coded C compiler.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
On 7/28/2026 9:26 PM, Keith Thompson wrote:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
On 2026-07-28 17:00, Johann 'Myrkraverk' Oskarsson wrote:
On 29/07/2026 4:54 AM, Chris M. Thomasson wrote:1) an integer constant expression with a value of 0.
say NULL is (void*)0xDEADBEEF, or whatever, for a system. Then for a >>>> NULL is required to be a null pointer constant, for which there are only >>>> two options,
or
2) such an expression converted to (void*)
As of C23, a null pointer constant can be an integer constant
expression with the value 0, such an expression cast to void*, or the
predefined constant nullptr. (POSIX imposes some additional
requirements.) `(void*)nullptr` is guaranteed to evaluate to a null
pointer, but it's not a null pointer constant. Note that `(void*)0`
is a null pointer constant, but it doesn't qualify as a definition of
the NULL macro. 7.1.2 requires any definition of an object-like
macro in the standard library to "expand to code that is fully
protected by parentheses where necessary, so that it groups in an
arbitrary expression as if it were a single identifier".
Any of `0`, `((void*)0)`, or `nullptr` would be a reasonable
expansion for NULL. Unreasonable but valid definitions include:
#define NULL ('/'/'/'-'/'/'/')
#define NULL (1+1==3)
Since 0xDEADBEEF doesn't have a value of 0, it doesn't qualify.
Right. It's easy (but incorrect) to assume a correlation between
the fact that a constant 0 (in source code) is a null pointer
constant, and the fact that all-bits-zero (during execution)
is very commonly a representation for a null pointer. There are
historical reasons, but the language is careful does not require
any particular representation for a runtime null pointer.
[...]
The bits of the raw NULL does not have to be 0.
You're restating something that was already clearly stated in this
thread, and you're doing it incorrectly.
The NULL macro is a source code construct. It doesn't have "bits",
unless you count the bits making up the characters 'N', 'U', 'L',
'L' (in the source character set, which needn't match the execution
character set).
The thing whose bits don't have to be 0 is a null pointer *value*,
something that exists during execution and can result from an
occurence of NULL in the source code.
The point from upthread is that your suggested (void*)0xDEADBEEF
is not a null pointer constant, and therefore is not a valid
expansion of the NULL macro, *even if* 0xDEADBEEF matches the
run-time representation of a null pointer.
(I normally ignore your posts, but I made an arbitrary exception
in this case.)
Can a pointer have a value of zero, but not be a null pointer?
On 29/07/2026 23:33, Keith Thompson wrote:
The NULL macro is a source code construct. It doesn't have "bits",
unless you count the bits making up the characters 'N', 'U', 'L',
'L' (in the source character set, which needn't match the execution
character set).
The thing whose bits don't have to be 0 is a null pointer *value*,
something that exists during execution and can result from an
occurence of NULL in the source code.
The point from upthread is that your suggested (void*)0xDEADBEEF
is not a null pointer constant, and therefore is not a valid
expansion of the NULL macro, *even if* 0xDEADBEEF matches the
run-time representation of a null pointer.
(I normally ignore your posts, but I made an arbitrary exception
in this case.)
Can a pointer have a value of zero, but not be a null pointer?
Can a pointer have a value of zero, but not be a null pointer?
This, for example, thinks that p ends up as a null pointer.
But there is no constant 0.
Probably I'm confused.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *p = malloc(1);
printf("p = %p\n", (void*) p);
do
{
p--;
printf("p is%s null, p = %p\n", p ? " not" : "", (void*) p);
}
while (p);
return 0;
}
On Thu, 30 Jul 2026 07:40:04 +0100, Richard Harnden wrote:
Can a pointer have a value of zero, but not be a null pointer?
C doesn?t define any special pointer values, other than the null
pointer. The null pointer can be compared for equality with (a
suitable cast of) the integer literal 0 (I?m not sure, it might be
that arbitrary integer expressions evaluating to 0 are not allowed),
but that is not considered grounds for concluding/requiring that the
bit pattern for the null pointer is actually equal to the integer
value 0.
On Thu, 30 Jul 2026 07:40:04 +0100, Richard Harnden wrote:
Can a pointer have a value of zero, but not be a null pointer?
C doesn?t define any special pointer values, other than the null
pointer. The null pointer can be compared for equality with (a
suitable cast of) the integer literal 0 (I?m not sure, it might be
that arbitrary integer expressions evaluating to 0 are not allowed),
but that is not considered grounds for concluding/requiring that the
bit pattern for the null pointer is actually equal to the integer
value 0.
void *np = (void*)0; // the cast isn't actually needed
int zero = 0;
void *fake_np = (void*)0;
can even assign different values to np and to fake_np.
... the value of a pointer is the location that it points at. It's
value is never a number.
On Thu, 30 Jul 2026 06:44:41 -0400, James Kuyper wrote:
... the value of a pointer is the location that it points at. It's
value is never a number.
In C, its value is never *directly compatible* with a number.
On 2026-07-30 06:54, Keith Thompson wrote:
void *np = (void*)0; // the cast isn't actually needed
int zero = 0;
void *fake_np = (void*)0;
I suspect that you intended that to be (void*)zero.
can even assign different values to np and to fake_np.
On Thu, 30 Jul 2026 06:44:41 -0400, James Kuyper wrote:
... the value of a pointer is the location that it points at. It's
value is never a number.
In C, its value is never *directly compatible* with a number.
On Thu, 30 Jul 2026 21:00:15 -0000 (UTC), I wrote:
On Thu, 30 Jul 2026 06:44:41 -0400, James Kuyper wrote:
... the value of a pointer is the location that it points at. It's
value is never a number.
In C, its value is never *directly compatible* with a number.
Except integer 0, of course.
On Thu, 30 Jul 2026 07:40:04 +0100, Richard Harnden wrote:
Can a pointer have a value of zero, but not be a null pointer?
C doesn?t define any special pointer values, other than the null
pointer. The null pointer can be compared for equality with (a
suitable cast of) the integer literal 0 (I?m not sure, it might be
that arbitrary integer expressions evaluating to 0 are not allowed),
but that is not considered grounds for concluding/requiring that the
bit pattern for the null pointer is actually equal to the integer
value 0.
On Thu, 30 Jul 2026 06:44:41 -0400, James Kuyper wrote:
... the value of a pointer is the location that it points at. It's
value is never a number.
In C, its value is never *directly compatible* with a number.
On 7/30/2026 2:00 PM, Lawrence D?Oliveiro wrote:
On Thu, 30 Jul 2026 06:44:41 -0400, James Kuyper wrote:
... the value of a pointer is the location that it points at. It's
value is never a number.
In C, its value is never *directly compatible* with a number.
Well, uintptr_t?
In article <114h1v7$2b0po$2@dont-email.me>,Afait uintptr_t can be set to the NULL and any pointer? Then compared?
Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
On 7/30/2026 2:00 PM, Lawrence D?Oliveiro wrote:
On Thu, 30 Jul 2026 06:44:41 -0400, James Kuyper wrote:
... the value of a pointer is the location that it points at. It's
value is never a number.
In C, its value is never *directly compatible* with a number.
Well, uintptr_t?
That's a type, not a value.
On 7/30/2026 7:43 PM, Dan Cross wrote:
In article <114h1v7$2b0po$2@dont-email.me>,Afait uintptr_t can be set to the NULL and any pointer? Then compared?
Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
On 7/30/2026 2:00 PM, Lawrence D?Oliveiro wrote:
On Thu, 30 Jul 2026 06:44:41 -0400, James Kuyper wrote:
... the value of a pointer is the location that it points at. It's
value is never a number.
In C, its value is never *directly compatible* with a number.
Well, uintptr_t?
That's a type, not a value.
The contexts where a conditional expression yields a Bool that is usedThe standard does not describe the behavior of those constructs in terms
for conditional branching are these:
if (cond)
while (cond)
do while(cond)
for(;cond;)
cond?: (can use branching)
bart <bc@freeuk.com> writes:
[...]
The contexts where a conditional expression yields a Bool that is usedThe standard does not describe the behavior of those constructs in terms
for conditional branching are these:
if (cond)
while (cond)
do while(cond)
for(;cond;)
cond?: (can use branching)
of a conversion to bool. There's a separate description for each of
those constructs, all of which depend upon whether "the controlling expression compares unequal to 0".
"When any scalar value is converted to bool, the result is false if the
value is a zero (for arithmetic types), null (for pointer types), or the scalar has type nullptr_t; otherwise, the result is true." (6.3.3.2p1)
or the scalar has type nullptr_t;
On 31/07/2026 19:21, James Kuyper wrote:
bart <bc@freeuk.com> writes:
[...]
The contexts where a conditional expression yields a Bool that is usedThe standard does not describe the behavior of those constructs in terms
for conditional branching are these:
if (cond)
while (cond)
do while(cond)
for(;cond;)
cond?: (can use branching)
of a conversion to bool. There's a separate description for each of
those constructs, all of which depend upon whether "the controlling
expression compares unequal to 0".
The result of such a compare would be 0 or 1, which is what I loosely
call a Bool.
(In practice it is unlikely that an actual 0 or 1 is ever generated,
and which is then subsequently tested. But it can happen, certainly
within intermediate stages before the final code.)
"When any scalar value is converted to bool, the result is false if the
value is a zero (for arithmetic types), null (for pointer types), or the
scalar has type nullptr_t; otherwise, the result is true." (6.3.3.2p1)
If the scalar value is X, I like to think of it as evaluating !!X, or
just !X if it more conveniently suits the logic. Unless it knows that
X may be already be Bool (ie. an integer value of either 0 or 1,
perhaps the result of a compare op), then it can dispense with it.
This is even before an optimising compiler is let loose on it.
or the scalar has type nullptr_t;
That's new to me. So this is a type with only one possible value? How
would that be used?
Apologies. I did this in my text editor, and neglected to notice
the longest line is 81 columns. My editor says the end of the line
is at 80, but starts the column count at zero. I'm unsure if that
should be counted as 80 columns, or 81. Is the following any better
for you? The original displays correctly for me, in my newsreader.
__ .----.__ .________________________________________.
-' `/(#)#(#) `- . o O ( I tend to assume people are more
` (#)#(#) \ ___ ^( than they actually are, because it )^^
__( \ ,,,/ `. ``. ( makes them either feel better, or
/ \ \,-/ | \ ( times they rise to the challenge!
| `-- ( ( /__ ( ^^^( )^^^^^^^^^^^^^^^^^^^^^^^^^^^^
` ( `---\ `---._` ( } (^^
| | \ `----._`.`. .' ( For instance, this is an old ASCII
. ( `-) \ `. )) ) | ( horror I made several years ago.
/ \ / / ) / { ( I got semi-famous for my
/ \ / ( | ( ( of non-trivial ASCII art. )^^^^^^^^^^
/ ,\ /\\\ ( |_ \ ( And I made all of it by hand,
/ /\( ) / .`\\\ ^^( this thought bubble.
\\/ \ .-' | | -_ ^^^^^^^^^^^^^^^^^^^^^^^
\ .'___( ) \ `-.
-' \/\___\\__\
The result of such a compare would be 0 or 1, which is what I
loosely call a Bool.
On 7/30/2026 7:43 PM, Dan Cross wrote:
In article <114h1v7$2b0po$2@dont-email.me>,Afait uintptr_t can be set to the NULL and any pointer? Then compared?
Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
On 7/30/2026 2:00 PM, Lawrence D?Oliveiro wrote:
On Thu, 30 Jul 2026 06:44:41 -0400, James Kuyper wrote:
... the value of a pointer is the location that it points at. It's
value is never a number.
In C, its value is never *directly compatible* with a number.
Well, uintptr_t?
That's a type, not a value.
| Sysop: | Jacob Catayoc |
|---|---|
| Location: | Pasay City, Metro Manila, Philippines |
| Users: | 4 |
| Nodes: | 4 (0 / 4) |
| Uptime: | 496009:12:23 |
| Calls: | 178 |
| Files: | 605 |
| D/L today: |
22 files (18,741K bytes) |
| Messages: | 70,152 |