Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

New features added into C23 standard

304 views
Skip to first unread message

Thiago Adams

unread,
Jul 22, 2022, 3:32:12 PM7/22/22
to
- nullptr
- typed enumerations
- improved normal enumerations
- Comma omission and comma deletion
- constexpr
- auto
- TIME_MONOTONIC
- bit-precise bit-fields
- relaxed requirements for variadic parameter lists
- embed
...


Ben Bacarisse

unread,
Jul 22, 2022, 6:15:26 PM7/22/22
to
At the late stage, presumably this is all agreed material?

--
Ben.

Malcolm McLean

unread,
Jul 23, 2022, 4:31:16 AM7/23/22
to
Comma omission and comma deletion?

Kenny McCormack

unread,
Jul 23, 2022, 6:10:59 AM7/23/22
to
In article <916ae40e-0ec2-4acc...@googlegroups.com>,
Check out:

https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2160.htm

--
They say compassion is a virtue, but I don't have the time!

- David Byrne -

Thiago Adams

unread,
Jul 25, 2022, 4:49:30 PM7/25/22
to
It's hard to believe how nullptr was accepted.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3039.htm
The same for auto and constexpr.

Thiago Adams

unread,
Jul 25, 2022, 4:57:45 PM7/25/22
to
In 2018 I suggested nullptr

https://groups.google.com/g/comp.lang.c/c/YJmom4rC2FI/m/DJzGmwXmCQAJ

In 2019 I replied my own message saying that was a bad idea.
https://groups.google.com/g/comp.lang.c/c/YJmom4rC2FI/m/Daz0AvxHAwAJ

I agree with myself from 2019.
"
One difference between adding _Bool into the language is that BOOL
was never added into the language headers. (Am I right? I didn't find
BOOL or TRUE FALSE at the language specification).

On the other hand if you search for NULL at the standard you will
find that NULL is defined in stddef.h

This means that adding _Bool didn't added a new way to declare booleans,
but adding nullptr will create this confusion that is two ways of
writing code that means NULL.

In C we also have different ((void*)0) conversion rules compared with
C++ that is more strict.

Then for C, I believe that the literal that corresponds nullptr already
exists and it is ((void*)0).

What could be done is adding flags to static analysis to decide what
to do with ((void*)0) conversions.
"

Keith Thompson

unread,
Jul 25, 2022, 5:50:41 PM7/25/22
to
I don't see the problem. There are already arbitrarily many ways to
write a null pointer constant in C. For example, '\0' '-'-'-', and
0x0ULL are all null pointer constants.

A null pointer constant can be of any integer type, or of type void*.

Adding nullptr and nullptr_t adds one more form of null pointer
constant, and an unambiguous type for nullptr. Of course NULL and
(void*)0 are still valid, because invalidating them would break tons of
existing code -- but nullptr is the new preferred way to write a null
pointer constant. Old code is still valid, but new code can be cleaner
-- and if a user writes nullptr, there's no question that it was
*intended* to be a null pointer constant, which could result in clearer
diagnostic messages.

(It's likely that if the language were being defined from scratch,
nullptr would be the *only* way to write a null pointer constant. The
stuff about using integer constant expressions exists only for backward
compatibility, and we're stuck with it.)

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

Thiago Adams

unread,
Jul 25, 2022, 6:51:54 PM7/25/22
to
I cannot see any problem with (void*) 0.

Apparently this was considered but I didn't found details about the problem.

"After WG14 refused a specification for a simple macro with value (void*)0, as well as a sophisticated
version with an incomplete type and with a rewriting approach for many contexts, this new version tries
a middle ground."

I never see anyone complaining or having bugs with NULL. And the original motivation
for C++ was different.

Keith Thompson

unread,
Jul 25, 2022, 9:05:00 PM7/25/22
to
I think what WG14 refused was defining nullptr as a macro expanding to
((void*)0). Given that NULL is commonly defined that way, there
wouldn't have been much point.

> I never see anyone complaining or having bugs with NULL. And the original motivation
> for C++ was different.

One example: The (POSIX, not ISO C) execl() function requires its last
argument to be a null pointer, marking the end of the arguments.

int execl(const char *pathname, const char *arg, ...
/* (char *) NULL */);

If you don't cast the NULL argument to an appropriate type, you have
undefined behavior, because it can be of integer type (typically int if
NULL is defined as 0).

That particular problem could be addressed by requiring NULL to be
defined as exactly ((void*)0). I don't know whether that would leave
problems, but that could just be a lack of imagination on my part.
(Incidentally, C23 updates the section on parenthesized expressions to
make it clear that a parenthesized null pointer constant is a null
pointer constant.)

Just as a matter of personal preference, I like the idea of having a
null pointer constant built into the language, as a lot of other
languages do. Note that with nullptr as a keyword, you don't have to
include any header to get a null pointer constant (or remember which
headers define NULL).

Thiago Adams

unread,
Jul 25, 2022, 10:23:33 PM7/25/22
to
I would like to have NULL as keyword and defined as ((void*) 0).














Opus

unread,
Jul 25, 2022, 11:13:54 PM7/25/22
to
I think the whole idea with C is to break as little existing code as
possible.

Since NULL was never required to be exactly '(void *) 0' before, making
it so in C23 would thus potentially break existing code. (Not saying
that the code it would potentially break would not be questionable, but
just the way it is.)

So, I get the idea of defining a new keyword here and not "breaking" the
existing NULL. Adding new 'features' is OK, changing the definition of
an existing feature in the standard is a lot more difficult. That's
actually what has made the strength of C and I can understand why the
commitee wouldn't want to break that.

With that said, if anything, the existence itself of a "null" pointer is
questionable. It's usually used as an "invalid" pointer (guaranteed to
be unambiguously invalid), but the fact its value is tied to '0' (and I
don't think nullptr changes this?) can be problematic. I think the
underlying value should be made platform-dependent. If that's what
nullptr is, good, but I am not sure.


Bonita Montero

unread,
Jul 25, 2022, 11:36:18 PM7/25/22
to
I don't know why this language is still extended.
These are all concepts that can no longer be saved.
C is just a hopelessly outdated language.

Richard Damon

unread,
Jul 25, 2022, 11:37:05 PM7/25/22
to
First, some program may correctly depend on an implementation guarentee
that NULL was a value of 0, which has been perfectly valid in the past.
Just because it make the program non-portable, doesn't automatically
make it bad.

Second, as to NULL pointers having the value 0, they don't. Only an
actual constant value of 0 gets converted to a NULL pointer value, and
expression that just happens to be 0 doesn't (if you force it with a
cast, it MAY become a NULL pointer, but that isn't actually guarenteed
by the standard as a remember).

When converting a pointer value to a bool, a NULL pointer get converted
to 0, and non-HULL pointers to 1. What happens with a pointer value that
is all bits 0 depends on if that just happens to be a NULL pointer or not.

Yes, there are a lot of things that make it hard on an implementation if
all bits 0 isn't a NULL pointer, but nothing says it actually has to
be), unless I have missed something new.

Andrey Tarasevich

unread,
Jul 26, 2022, 12:53:09 AM7/26/22
to
On 7/25/2022 8:13 PM, Opus wrote:
>
> With that said, if anything, the existence itself of a "null" pointer is
> questionable. It's usually used as an "invalid" pointer (guaranteed to
> be unambiguously invalid), but the fact its value is tied to '0' (and I
> don't think nullptr changes this?) can be problematic.
>

Null pointer constant (NPC) is "tied to 0" at source code level only.
The underlying physical value - null pointer value - is not zero/does
not have to be zero. The underlying physical value has always been
implementation-dependent. There's nothing problematic here and there's
nothing to change.

--
Best regards,
Andrey

Malcolm McLean

unread,
Jul 26, 2022, 2:00:06 AM7/26/22
to
There are two issues.
One is that on some architectures, writing to memory address zero is a
valid thing to do. Whilst you can argue that, in that case, a null pointer
should not be all bits zero, in reality it will be, and it will be obvious from
context where a write to address zero is intended.

The other one is that you often have pointers embedded in structures,
which you zero-initialise. Again, you can argue that this is wrong because
a null pointer isn't necessarily all bits zero, but it is a widely used practice.
Also the struct s = {0}; syntax implies that null == zero, even though it
doesn't actually require that.

Öö Tiib

unread,
Jul 26, 2022, 3:48:15 AM7/26/22
to
Said corner case (when accessing memory at address zero is actually
intended) should happen only to people who are implementing
executive environment on such architecture. That means they are on
side of providers of whatever guarantees not on side of consumers
of those.

> The other one is that you often have pointers embedded in structures,
> which you zero-initialise. Again, you can argue that this is wrong because
> a null pointer isn't necessarily all bits zero, but it is a widely used practice.
> Also the struct s = {0}; syntax implies that null == zero, even though it
> doesn't actually require that.

Nothing implies that struct s = {nullptr}; results with s having all bits zero.
When all platforms that the code is targeting guarantee that it results (and
it matters) then there is nothing to worry. When there is doubt (and as you
already said it has likely no ground) then static_assert has been in
<assert.h> for decade.

Malcolm McLean

unread,
Jul 26, 2022, 5:08:55 AM7/26/22
to
You often see memset(&s, 0, sizeof(struct s)); Microsoft even provide a function called
ZeroMemory() to make this a bit more explicit. It's expected that any embedded pointers
will be set to null. If we treat nullptr as an abstract symbol rather than a bit pattern,
then it's less obvious what this code is doing. However you can argue that it is
broken anyway. But you'd be hard-pressed to find an architecture on which it
actually breaks.

Richard Damon

unread,
Jul 26, 2022, 6:59:47 AM7/26/22
to
On 7/26/22 5:08 AM, Malcolm McLean wrote:
> You often see memset(&s, 0, sizeof(struct s)); Microsoft even provide a function called
> ZeroMemory() to make this a bit more explicit. It's expected that any embedded pointers
> will be set to null. If we treat nullptr as an abstract symbol rather than a bit pattern,
> then it's less obvious what this code is doing. However you can argue that it is
> broken anyway. But you'd be hard-pressed to find an architecture on which it
> actually breaks.

It may be "expected", but in general it isn't defined to be, even though
it will work on most machines.

And today it probably works on most machines because it is expected to
work and more efficient if it is that way. Some machines even bent them
selves a bit to make sure that an all zero pointer was valid as a NULL
pointer value.

Öö Tiib

unread,
Jul 26, 2022, 7:02:49 AM7/26/22
to
Computer architectures and operating systems can provide guarantees that
are not in C standard. Programmers can use those. But why should C standard
dictate requirements to underlying architecture or operating system? It is not
in its sphere of influence.

> It's expected that any embedded pointers will be set to null.

It is reasonable expectation that is guaranteed by things outside of C standard.

> If we treat nullptr as an abstract symbol rather than a bit pattern,
> then it's less obvious what this code is doing. However you can argue that it is
> broken anyway. But you'd be hard-pressed to find an architecture on which it
> actually breaks.

Then I'd look C FAQ question 5.17 <https://c-faq.com/null/machexamp.html>
Even when that would become clear that such platforms are inferior somehow,
(how?) then why it matters? C standard is not meant to address everything in
our industry.

Richard Damon

unread,
Jul 26, 2022, 7:04:53 AM7/26/22
to
On many machines there isn't a pointer value that can't be used to
access memory, so SOME address needs to be chosen.

Also, on most machines, location 0 is unlikely to be data or a function
to call, as it is defined by the archtecture to be something special, so
a "user" program won't be dealing with a pointer to absolute location 0.

Malcolm McLean

unread,
Jul 26, 2022, 8:25:05 AM7/26/22
to
C requires that ptr + 1 be representable if ptr is valid. So on a 16 bit machine,
0xFFFF cannot be used as a valid char * (unless you really do put the compiler
through contortions). All bits set would therefore be the obvious value to use for
the null pointer.

Scott Lurndal

unread,
Jul 26, 2022, 9:27:16 AM7/26/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:
>On Monday, 25 July 2022 at 22:50:41 UTC+1, Keith Thompson wrote:
>> Thiago Adams <thiago...@gmail.com> writes:
>
>>
>> (It's likely that if the language were being defined from scratch,
>> nullptr would be the *only* way to write a null pointer constant. The
>> stuff about using integer constant expressions exists only for backward
>> compatibility, and we're stuck with it.)
>>
>There are two issues.
>One is that on some architectures, writing to memory address zero is a
>valid thing to do. Whilst you can argue that, in that case, a null pointer
>should not be all bits zero, in reality it will be, and it will be obvious from
>context where a write to address zero is intended.

Indeed, I worked on one system where the a NULL pointer was defined (by hardware) as a
32-bit (8 digit) field where the low-order 24 bits (6 digits) are all ones.

The search linked list (SLT) instruction could return a NULL pointer
on a miss, for example.

The system never had a C compiler, but had a Modula-like HLL called SPRITE.

Keith Thompson

unread,
Jul 26, 2022, 2:40:20 PM7/26/22
to
Thiago Adams <thiago...@gmail.com> writes:
[...]
> I would like to have NULL as keyword and defined as ((void*) 0).

If it were a keyword, it wouldn't be defined as anything.

If it were a macro (as it currently is), it wouldn't be a keyword.

I suppose you could have NULL both as a keyword and as a macro,
but I don't see the point of that.

Keith Thompson

unread,
Jul 26, 2022, 2:44:27 PM7/26/22
to
Opus <ifo...@youknew.org> writes:
[...]
> I think the whole idea with C is to break as little existing code as
> possible.
>
> Since NULL was never required to be exactly '(void *) 0' before,
> making it so in C23 would thus potentially break existing code. (Not
> saying that the code it would potentially break would not be
> questionable, but just the way it is.)

A small quibble: NULL cannot be `(void*)0`, because library macros are
required to be fully parenthesized. With that definition, `sizeof NULL`
would be parsed incorrectly.

It's commonly defined as `((void*)0)`. (Strictly speaking, the language
doesn't guarantee that a parenthesized null pointer constant is a null
pointer constant, but in practice it is, and C23 makes that explicit.)

> So, I get the idea of defining a new keyword here and not "breaking"
> the existing NULL. Adding new 'features' is OK, changing the
> definition of an existing feature in the standard is a lot more
> difficult. That's actually what has made the strength of C and I can
> understand why the commitee wouldn't want to break that.
>
> With that said, if anything, the existence itself of a "null" pointer
> is questionable. It's usually used as an "invalid" pointer (guaranteed
> to be unambiguously invalid), but the fact its value is tied to '0'
> (and I don't think nullptr changes this?) can be problematic. I think
> the underlying value should be made platform-dependent. If that's what
> nullptr is, good, but I am not sure.

The representation of the null pointer (null pointerS, actually, since
there are different pointer types) is unspecified. The link to 0 is at
the source code level only. If an implementation uses a representation
other than all-bits-zero for a null pointer, then the conversion in
`(void*)0` is non-trivial.

Note that converting a non-constant 0 to a pointer type is not
guaranteed to yield a null pointer value.

Most implementations use all-bits-zero for null pointers because it's
simpler.

Keith Thompson

unread,
Jul 26, 2022, 2:48:07 PM7/26/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:
[...]
> There are two issues.
> One is that on some architectures, writing to memory address zero is a
> valid thing to do. Whilst you can argue that, in that case, a null pointer
> should not be all bits zero, in reality it will be, and it will be obvious from
> context where a write to address zero is intended.

Adding nullptr to the language has nothing to do with that issue. All
the existing forms of null pointer constant are still valid.

> The other one is that you often have pointers embedded in structures,
> which you zero-initialise. Again, you can argue that this is wrong because
> a null pointer isn't necessarily all bits zero, but it is a widely used practice.
> Also the struct s = {0}; syntax implies that null == zero, even though it
> doesn't actually require that.

In other words, if you incorrectly assume that a null pointer is
all-bits-zero, you can make mistakes.

No, `struct s obj = {0};` doesn't imply anything about the
representation of a pointer -- at least not to someone who knows
the language well enough. Nor does it imply anything about the
representation of floating-point 0.0, which also is not guaranteed
to be all-bits-zero. And again, adding nullptr has nothing to do
with this.

Keith Thompson

unread,
Jul 26, 2022, 2:52:06 PM7/26/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:
[...]
> You often see memset(&s, 0, sizeof(struct s)); Microsoft even provide
> a function called ZeroMemory() to make this a bit more explicit. It's
> expected that any embedded pointers will be set to null. If we treat
> nullptr as an abstract symbol rather than a bit pattern, then it's
> less obvious what this code is doing. However you can argue that it is
> broken anyway. But you'd be hard-pressed to find an architecture on
> which it actually breaks.

Yes, you can write non-portable code if you're targeting an
implementation that makes additional guarantees beyond those provided by
the language standard.

If your code will only run under an implementation that guarantees that
null pointers and floating-point zeros are represented as all-bits-zero,
you can safely zero your structures and assume that its members will
have the values you want.

Non-portable code isn't necessarily wrong or "broken". It's just
non-portable.

Kaz Kylheku

unread,
Jul 26, 2022, 4:28:06 PM7/26/22
to
On 2022-07-26, Keith Thompson <Keith.S.T...@gmail.com> wrote:
> Malcolm McLean <malcolm.ar...@gmail.com> writes:
> [...]
>> There are two issues.
>> One is that on some architectures, writing to memory address zero is a
>> valid thing to do. Whilst you can argue that, in that case, a null pointer
>> should not be all bits zero, in reality it will be, and it will be obvious from
>> context where a write to address zero is intended.
>
> Adding nullptr to the language has nothing to do with that issue. All
> the existing forms of null pointer constant are still valid.
>
>> The other one is that you often have pointers embedded in structures,
>> which you zero-initialise. Again, you can argue that this is wrong because
>> a null pointer isn't necessarily all bits zero, but it is a widely used practice.
>> Also the struct s = {0}; syntax implies that null == zero, even though it
>> doesn't actually require that.
>
> In other words, if you incorrectly assume that a null pointer is
> all-bits-zero, you can make mistakes.

It's possible that you can assume that a pointer is all zero bits,
and never be actually incorrect for your entire programming career.

> No, `struct s obj = {0};` doesn't imply anything about the
> representation of a pointer -- at least not to someone who knows

The problem is that "struct s obj = { 0 }" is insecure if the image of s
can be communicated outside of the program; it doesn't guarantee that
the padding between struct members and at the end will be obliterated
with zeros, and those areas can leak information.

This is secure version (at least if we momentarily ignore the
squabbling about whether any memset call can actually be secure):

struct s;
memset(&s, 0, sizeof s);

Or else, get a fresh object from calloc. Of course, this sort
of hedge is possible:

struct s;
memset(&s, 0, sizeof s);
#if CONFIG_NULL_ISNT_ZERO
s.ptr = NULL;
#endif

and other such. I've never seen it in the wild.

--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

Richard Damon

unread,
Jul 26, 2022, 10:30:28 PM7/26/22
to
Is it required anywhere that the address+1 have a value > then the value

If not, a ptr value of all 1's could be a valid location, and the +1 be
the all 0's value. (likely requiring that this sort of pointer
arithmetic act modulo like unsigned).

I suppose doing this would break a lot of programs that assume that the
ptr to end+1 is greater than all valid addresses in the array.

Lynn McGuire

unread,
Jul 28, 2022, 5:47:15 PM7/28/22
to
Not for the embedded people. They live by C and love it.

Lynn


Ben Bacarisse

unread,
Jul 28, 2022, 7:03:07 PM7/28/22
to
Something went wrong with this, I think!

> If not, a ptr value of all 1's could be a valid location, and the +1
> be the all 0's value. (likely requiring that this sort of pointer
> arithmetic act modulo like unsigned).
>
> I suppose doing this would break a lot of programs that assume that
> the ptr to end+1 is greater than all valid addresses in the array.

The first part is confusing so I am not sure what you are saying here,
but I think it needs clarifying. That assumption, that end+1 will
compare greater than any pointer to an array element, is a valid
assumption, so breaking programs that make it would be wrong.

--
Ben.

Richard Damon

unread,
Jul 28, 2022, 9:21:11 PM7/28/22
to
The point is that just as all 0's could be a valid location to access,
so could all 1's be a valid location.

And, in fact, on some machines all ones might not be a valid address to
put in a pointer for some types as it is mis-aligned, and just looking
at a mis-aligned address might be a trapping operation.

IS it a valid assumption, does that Standard actually promise it? I will
admit, it is a common assumption, but as you point out, it means that
the last location of memory can't be a valid location for an object, as
ALL objects can be thought of as part of an array, and that array has a
valid last loctation + 1 value.

William Ahern

unread,
Jul 28, 2022, 10:45:19 PM7/28/22
to
If you mean "ALL objects can be thought of as part of [the same] array",
then you're wrong. Relational operators like ">" aren't defined for objects
that aren't contained within the same aggregate object (arrays and
structures), and more importantly they're not defined for the null pointer;
only "==" is defined for the null pointer. We can't arrive at a place where
this relationship could matter.

Nonetheless, it stands to reason that whatever the valid values for a null
pointer, their representation(s) must be distinguishable from any valid
object pointer value, though only for the purposes of testing equality "=="
using validly derived operands. This restriction might be relevant to how
objects are allocated (i.e. to malloc), but it might not be--the
representation needn't be visible within the runtime environment, not even
through type punning or char pointer introspection.

Consider the CHERI project, which transparently utilizes an address tagging
scheme, 1 bit of which isn't addressable or visible. (Pointers logically
have 129 bits, but only 128 bits are represented in addressable--directly or
indirectly--memory.) Using a similar tagging scheme, I could imagine a
system where a null pointer could have any and every visible representation,
it's null'ness signaled by its out-of-band tag. This might cause headaches
with conversions to intptr_t, but strictly speaking intptr_t is optional,
and if it was supported than naturally that would imply something about the
size of the addressable space or the size of intptr_t relative to object
pointer types.

Bonita Montero

unread,
Jul 29, 2022, 1:13:18 AM7/29/22
to
When I look at jobs for embedded developers I find more
jobs which ask for C++ skills than C only.

Ben Bacarisse

unread,
Jul 29, 2022, 10:17:32 AM7/29/22
to
All zeros is not an issue provided the compiler does the right thing
with mull pointers and the various conversions.

All ones is more problematic because a pointer just past the object at
that address must be valid and compare greater than the all ones
pointer. This could be achieved by some very weird fiddling in the
implementation, but from the programmer's point of view, if you have a
valid object pointer p, p+1 must compare greater than p, though access
through p+1 is undefined.

> And, in fact, on some machines all ones might not be a valid address
> to put in a pointer for some types as it is mis-aligned, and just
> looking at a mis-aligned address might be a trapping operation.

Sure. In those cases even all ones minus one would not be a valid place
to put an object because p+1 must always be a trap-free pointer (that
compares greater than p).

> IS it a valid assumption, does that Standard actually promise it?

Yes.

> I will admit, it is a common assumption, but as you point out, it
> means that the last location of memory can't be a valid location for
> an object, as ALL objects can be thought of as part of an array, and
> that array has a valid last loctation + 1 value.

All objects are not part of an array. You can /think/ of them as being
part of some giant array, but you'd end up with undefined behaviour
depending on how you acted based on that model. For example, the
standard defines <, <=, > and >= only for pointers to objects contained
in some larger C object. Comparing

int x, y;
if (&x > &y) ...

is undefined by the language standard, even if you choose to think of
everything as being inside some larger object.

--
Ben.

Scott Lurndal

unread,
Jul 29, 2022, 12:39:26 PM7/29/22
to
William Ahern <wil...@25thandClement.com> writes:
>Richard Damon <Ric...@damon-family.org> wrote:

>
>Consider the CHERI project, which transparently utilizes an address tagging
>scheme, 1 bit of which isn't addressable or visible. (Pointers logically
>have 129 bits, but only 128 bits are represented in addressable--directly or
>indirectly--memory.)

In Cheri they're more than pointers, they're capabilities that describe
the bounds and access constraints along with the memory address.

Specifically, the implementation is required to support one extra bit
for each 128-bits of DRAM. This can be out-of-band (i.e. a DRAM
region not visible to the CPU load/store instructions) or with special
DIMMs (pointers must be 128-bit aligned).

The bit indicates that the corresponding 128-bit region contains a
capability (and can only be modified using specific instructions).


>Using a similar tagging scheme, I could imagine a
>system where a null pointer could have any and every visible representation
>it's null'ness signaled by its out-of-band tag.

In CHERI, a NULL pointer has a specific representation in the
capability (which is not necessarily all zeros or all ones).

Vir Campestris

unread,
Jul 29, 2022, 4:18:49 PM7/29/22
to
On 26/07/2022 12:02, Öö Tiib wrote:
> Then I'd look C FAQ question 5.17<https://c-faq.com/null/machexamp.html>
> Even when that would become clear that such platforms are inferior somehow,
> (how?) then why it matters? C standard is not meant to address everything in
> our industry.

I could add another architecture into that FAQ - if I had an account.

<https://en.wikipedia.org/wiki/ICL_2900_Series#Addressing_mechanisms>

describes the ICL 2900 descriptor register. This contains address, data
size of target, array index limit, and various other stuff.

Setting DR to all zeroes would give a trap on access luckily.

I wrote assembler for it _many_ years ago, but never C.

Andy

Vir Campestris

unread,
Jul 29, 2022, 4:21:41 PM7/29/22
to
On 29/07/2022 06:14, Bonita Montero wrote:
>
> When I look at jobs for embedded developers I find more
> jobs which ask for C++ skills than C only.

I just retired from the embedded world.

Although most of my last job was C++, a good knowledge of C was required.

Smaller systems tend to use C more. We had megabytes of store, not
kilobytes.

It depends where you want your career to go.

Andy

Richard Damon

unread,
Jul 29, 2022, 6:55:17 PM7/29/22
to
Nope, the fact that all objects, even if not in an explicit array, can
be thought of as a member of an array of size 1, and thus that "address
+ 1" of that object is valid.

Thus for ANY object, (&obj)[0] references the object, and &((&obj)[1])
is a valid address, as the one past end of an array.

Keith Thompson

unread,
Jul 29, 2022, 7:55:52 PM7/29/22
to
Vir Campestris <vir.cam...@invalid.invalid> writes:
> On 26/07/2022 12:02, Öö Tiib wrote:
>> Then I'd look C FAQ question 5.17<https://c-faq.com/null/machexamp.html>
>> Even when that would become clear that such platforms are inferior somehow,
>> (how?) then why it matters? C standard is not meant to address everything in
>> our industry.
>
> I could add another architecture into that FAQ - if I had an account.
>
> <https://en.wikipedia.org/wiki/ICL_2900_Series#Addressing_mechanisms>
>
> describes the ICL 2900 descriptor register. This contains address,
> data size of target, array index limit, and various other stuff.

There is no "account". Steve Summit maintains the FAQ personally.

> Setting DR to all zeroes would give a trap on access luckily.
>
> I wrote assembler for it _many_ years ago, but never C.

David Brown

unread,
Jul 30, 2022, 8:42:56 AM7/30/22
to
On 26/07/2022 00:51, Thiago Adams wrote:
> I cannot see any problem with (void*) 0.
>
> Apparently this was considered but I didn't found details about the problem.
>
> "After WG14 refused a specification for a simple macro with value (void*)0, as well as a sophisticated
> version with an incomplete type and with a rewriting approach for many contexts, this new version tries
> a middle ground."
>
> I never see anyone complaining or having bugs with NULL. And the original motivation
> for C++ was different.

As I see it, the point of "nullptr" is not because there is something
wrong with NULL, 0, or "(void*) 0". It is because "nullptr" is
/better/, and gives you a clearer way to write your code, more
opportunities for checking, and thus lower risk of errors in your code
(for those that take advantage of the new feature and appropriate
tools). It does this without affecting existing code.

The key problem with null pointers in C code is that "0" is a null
pointer as well as an integer constant. So it's easy to mix up these
very different purposes when writing or reading code. (Let's be honest
here - most null pointers in real code are written "0", not "NULL".)

If you get in the habit of using "nullptr" as your null pointer, that
mixup is gone. And your tools can check this, giving a warning whenever
"0" is used in the context of a pointer.

"nullptr" is not as useful in C as it is in C++, since there is no
function overloading and thus no need to determine if "foo(0)" meant an
integer parameter or a pointer parameter, but it might still be handy
with _Generic.

Scott Lurndal

unread,
Jul 30, 2022, 11:18:31 AM7/30/22
to
Actually, I've been programming for quite a while in C and C++,
mainly in the OS/Hypervisor space, and I've not seen any use "0" instead of
NULL at the source level. Not since Unix v6, anyway.

I have seen common usage like this in modern code:

char *ptr = NULL;
...
if (ptr) ...

however, most coding guidelines we've used prefer 'if (ptr != NULL)',
particularly in C++.

Malcolm McLean

unread,
Jul 30, 2022, 12:13:46 PM7/30/22
to
Our code base uses 0 and NULL interchangeably, with no particular rules
or preferences. I've never found this to be a problem.

Ben Bacarisse

unread,
Jul 30, 2022, 12:31:45 PM7/30/22
to
Except for those programs that have decided to use that perfectly
ordinary name!

> The key problem with null pointers in C code is that "0" is a null
> pointer as well as an integer constant. So it's easy to mix up these
> very different purposes when writing or reading code.

The only problem I see is not really a case of confusing the two, and
that's using 0 in variable arguments where a pointer is expected. Here,
nullptr wins over the NULL because, at least technically, NULL might be
plain 0 (or some other constant integer expression)

The lowest impact change would have been to require the expansion of
NULL to be an expression of type void *. But I think the new committee
is inclined to go the C++ route: more changes, and more alignment, even
at the cost of some code breakage.

> (Let's be honest here - most null pointers in real code are written
> "0", not "NULL".)

Technically, using NULL is not a fix, since NULL /could/ expand to 0.
No implementation I know of does this (because of the var args problem)
but it's possible.

Using NULL might avoid what you see as the possibility of confusing the
two uses, but then I don't think I've come across that.

> If you get in the habit of using "nullptr" as your null pointer, that
> mixup is gone. And your tools can check this, giving a warning
> whenever "0" is used in the context of a pointer.

Bu the only case that really matters can't be warned against.

> "nullptr" is not as useful in C as it is in C++, since there is no
> function overloading and thus no need to determine if "foo(0)" meant
> an integer parameter or a pointer parameter, but it might still be
> handy with _Generic.

How? (I've not see the new draft so there may be something I',
missing...)

--
Ben.

Keith Thompson

unread,
Jul 30, 2022, 5:36:25 PM7/30/22
to
David Brown <david...@hesbynett.no> writes:
[...]
> The key problem with null pointers in C code is that "0" is a null
> pointer as well as an integer constant. So it's easy to mix up these
> very different purposes when writing or reading code. (Let's be
> honest here - most null pointers in real code are written "0", not
> "NULL".)
[...]

That doesn't match my experience at all. In C, I always use NULL for
null pointer constants.

(I seem to recall that Stroustrup advocated using 0 as a null pointer
constant before nullptr was introduced in C++11.)

Richard Damon

unread,
Jul 30, 2022, 6:15:52 PM7/30/22
to
On 7/30/22 5:36 PM, Keith Thompson wrote:
> David Brown <david...@hesbynett.no> writes:
> [...]
>> The key problem with null pointers in C code is that "0" is a null
>> pointer as well as an integer constant. So it's easy to mix up these
>> very different purposes when writing or reading code. (Let's be
>> honest here - most null pointers in real code are written "0", not
>> "NULL".)
> [...]
>
> That doesn't match my experience at all. In C, I always use NULL for
> null pointer constants.
>
> (I seem to recall that Stroustrup advocated using 0 as a null pointer
> constant before nullptr was introduced in C++11.)
>


C++ had the problem that (void *)0 didn't work for NULL as it doesn't
convert to other pointer types, while a constant expression 0 does.

Many C implementations defined it that way then.

Kaz Kylheku

unread,
Jul 30, 2022, 7:21:35 PM7/30/22
to
On 2022-07-30, Keith Thompson <Keith.S.T...@gmail.com> wrote:
> David Brown <david...@hesbynett.no> writes:
> [...]
>> The key problem with null pointers in C code is that "0" is a null
>> pointer as well as an integer constant. So it's easy to mix up these
>> very different purposes when writing or reading code. (Let's be
>> honest here - most null pointers in real code are written "0", not
>> "NULL".)
> [...]
>
> That doesn't match my experience at all. In C, I always use NULL for
> null pointer constants.

I dislike it that NULL may or may not just be zero. If it expands to
((void *) 0), then certain misuses of NULL fail to be diagnosed.

On the one hand, something silly like char x = NULL does get
diagnosed, while printf("%p", NULL) looks correct.

Take the code #define-NULL-0 platform, and this reverses; char x = NULL
is nonchalantly accepted, whereas if there are printf format string
diagnostics, printf("%p", NULL) warns.

Defining NULL as 0 should have been deprecated decades ago, with
implementations strongly encouraged to abandon the practice.

I'd be in favor of making NULL the language built-in; i.e.
use that as the spelling of nullptr.

Implementations could #define NULL NULL so that there is a macro.

Kaz Kylheku

unread,
Jul 30, 2022, 7:28:40 PM7/30/22
to
That makes no sense, though. A constant expression 1 does not convert
to pointer types in C++; the zero-valued expression only converts
because there is a hack in the language: that of the expression doubling
as a null pointer constant, just like in C.

Exactly the same hack can be implemented to allow (void *) 0 to
convert in situations where (void *) 1 won't.

C++ made a mess of the null pointer area; now the crap is seeping into C.

Keith Thompson

unread,
Jul 30, 2022, 7:46:46 PM7/30/22
to
Right. I wonder how much code defines nullptr as an identifer. I
suspect it's not much, especially given that it's a keyword in C++.

Except that I can imagine some programmers might do something like
#define nullptr ((void*)0)
just because they like the name. But a macro definition likely
wouldn't cause any problems.

[...]

>> "nullptr" is not as useful in C as it is in C++, since there is no
>> function overloading and thus no need to determine if "foo(0)" meant
>> an integer parameter or a pointer parameter, but it might still be
>> handy with _Generic.
>
> How? (I've not see the new draft so there may be something I',
> missing...)

nullptr is of the distinct type nullptr_t, which is a scalar type but
not a pointer or integer type, so a generic selection might have to
account for it.

(nullptr_t is not a keyword; rather it's defined in <stddef.h> as
something like `typeof(nullptr)`).

David Brown

unread,
Jul 31, 2022, 5:13:12 AM7/31/22
to
On 26/07/2022 09:48, Öö Tiib wrote:
> On Tuesday, 26 July 2022 at 09:00:06 UTC+3, Malcolm McLean wrote:
>> There are two issues.
>> One is that on some architectures, writing to memory address zero is a
>> valid thing to do. Whilst you can argue that, in that case, a null pointer
>> should not be all bits zero, in reality it will be, and it will be obvious from
>> context where a write to address zero is intended.
>
> Said corner case (when accessing memory at address zero is actually
> intended) should happen only to people who are implementing
> executive environment on such architecture. That means they are on
> side of providers of whatever guarantees not on side of consumers
> of those.
>

It is also not uncommon in microcontrollers for address zero to be a
useful address - and then it is user code that needs access. But it is
rarely an issue in practice. The most common situation is having it as
part of the flash for code, and it is typically part of the interrupt
vectors or reset vector. You would only want to read it for something
like a CRC check of the flash, and if you are concerned about the
compiler handling a pointer to address zero in an unhelpful manner, you
can use a pointer-to-volatile. The other cases I have seen are having
memory mapped peripherals there - and again, you always use volatile
accesses.

>> The other one is that you often have pointers embedded in structures,
>> which you zero-initialise. Again, you can argue that this is wrong because
>> a null pointer isn't necessarily all bits zero, but it is a widely used practice. >> Also the struct s = {0}; syntax implies that null == zero, even
though it
>> doesn't actually require that.
>
> Nothing implies that struct s = {nullptr}; results with s having all bits zero.

Indeed. The same applies to "struct s = { 0 };", if the first field is
a pointer. Logically, the fields are initialised exactly as they would
be if they were individual variables of the appropriate types.

> When all platforms that the code is targeting guarantee that it results (and
> it matters) then there is nothing to worry. When there is doubt (and as you
> already said it has likely no ground) then static_assert has been in
> <assert.h> for decade.
>

Richard Damon

unread,
Jul 31, 2022, 7:40:36 AM7/31/22
to
On 7/31/22 5:12 AM, David Brown wrote:
>
> It is also not uncommon in microcontrollers for address zero to be a
> useful address - and then it is user code that needs access.  But it is
> rarely an issue in practice.  The most common situation is having it as
> part of the flash for code, and it is typically part of the interrupt
> vectors or reset vector.  You would only want to read it for something
> like a CRC check of the flash, and if you are concerned about the
> compiler handling a pointer to address zero in an unhelpful manner, you
> can use a pointer-to-volatile.  The other cases I have seen are having
> memory mapped peripherals there - and again, you always use volatile
> accesses.

And for machine specific case like this, the fact that derefencing a
NULL pointer is "just" Undefined Behavior, that can be Implementaton
Defined to do what is wanted, as opposed to some how PROHIBITED.

*(char *0) = 0;

Is a fully legal statement that must be translated.

Yes, when you execute it, you get undefined behavior, but that might be
just simply defined to write to location 0.

Ben Bacarisse

unread,
Jul 31, 2022, 12:40:48 PM7/31/22
to
It doesn't /have/ to be translated (at least not to anything
meaningful). An implementation that uses location 0 might do the
obvious thing, but a compiler that translated that to puts("Sorry,
no."); would be conforming.

--
Ben.

Richard Damon

unread,
Jul 31, 2022, 12:56:27 PM7/31/22
to
And by that definition, ANY access to ANY specific location might not
work and be conforming.

The mapping of integer numbers to actually memory addresses is
implementation defined, and there is NO guarantee that any given address
can be created by code.

If you are writing code that needs to access specific addresses, you
need a compiler that defines the way to access those specific addresses,
and that includes acccessing location 0.

This is the way that Standard has always been. Some very useful stuff is
placed behind implementation dependency as that is simpler that adding a
lot of words for things that you might want on some systems but just
doesn't work on others.

Ben Bacarisse

unread,
Jul 31, 2022, 2:28:48 PM7/31/22
to
Richard Damon <Ric...@Damon-Family.org> writes:

> On 7/31/22 12:40 PM, Ben Bacarisse wrote:
>> Richard Damon <Ric...@Damon-Family.org> writes:
>>
>>> On 7/31/22 5:12 AM, David Brown wrote:
>>>> It is also not uncommon in microcontrollers for address zero to be a
>>>> useful address - and then it is user code that needs access.  But it is rarely an issue in practice.  The most common situation is having it as
>>>> part of the flash for code, and it is typically part of the interrupt vectors or reset vector.  You would only want to read it for something
>>>> like a CRC check of the flash, and if you are concerned about the compiler handling a pointer to address zero in an unhelpful manner, you
>>>> can use a pointer-to-volatile.  The other cases I have seen are having memory mapped peripherals there - and again, you always use volatile
>>>> accesses.
>>>
>>> And for machine specific case like this, the fact that derefencing a
>>> NULL pointer is "just" Undefined Behavior, that can be Implementaton
>>> Defined to do what is wanted, as opposed to some how PROHIBITED.
>>>
>>> *(char *0) = 0;
>>>
>>> Is a fully legal statement that must be translated.
>>>
>>> Yes, when you execute it, you get undefined behavior, but that might
>>> be just simply defined to write to location 0.
>> It doesn't /have/ to be translated (at least not to anything
>> meaningful). An implementation that uses location 0 might do the
>> obvious thing, but a compiler that translated that to puts("Sorry,
>> no."); would be conforming.
>i
> And by that definition, ANY access to ANY specific location might not
> work and be conforming.
>
> The mapping of integer numbers to actually memory addresses is
> implementation defined, and there is NO guarantee that any given
> address can be created by code.

I'm not sure why you think this is a problem. Sure, you can't pull any
value you like out of the air and assume that you can access it, but so
what? Any valid void * can be converted to, say, uintptr_t and back
again, so there /are/ integers that map to pointers in every conforming
implementation.

> If you are writing code that needs to access specific addresses, you
> need a compiler that defines the way to access those specific
> addresses, and that includes acccessing location 0.

Of course. I thought I covered that. Maybe I was unclear. If you need
access to specific location, your implementation should permit that.

> This is the way that Standard has always been. Some very useful stuff
> is placed behind implementation dependency as that is simpler that
> adding a lot of words for things that you might want on some systems
> but just doesn't work on others.

I think we may be agreeing with each other about most of this. I only
took issue with the notion that an undefined access must be translated
(and, by implication, translated "as expected").

--
Ben.

Richard Damon

unread,
Jul 31, 2022, 2:52:25 PM7/31/22
to
The key point is that *IF* the implementation defines how to access a
specific location of memory, and you use that method, then the
implementation must honor its definition.

That means that if they define how to convert a "number" to and address,
and don't exclude this working for address 0, then by that definition,
they can't "trap" accesses to the NULL pointer (if generating said null
pointer was done by the rules to access memory location 0).

The Standard may have left that access as explicit Undefined Behavior,
but by the implementation defining the Behavior, they need to honor
their definition.

Note uintprt_t might not exist, and might not be a simple mapping of
interger value to memory address, so that doesn't say that there exists
intergers that map to pointers on EVERY conforming implementation.

Yes, if uintptr_t exists, then every VALID pointer value can be
converted to an integer. This doesn't mean that every possible memory
address in the machine can be converted, as the program might not be
able to actually access every possible location in the machine (small
memory models in segmented architectures are an example).

There is also no promise that all values of a uintptr_t are valid pointers.

It also means that there might not be a value of uintptr_t to access
location 0 if the implementation doesn't consider a pointer to location
0 valid (and if it exists, it might not be the value 0).

So we get back to the point that we can only get defined access to
location 0 if the implementation defines it, and we do it in the way the
implementation defines.

Yes, for many implementations, that will end up being accessing through
a NULL pointer, but if we are to count on that working, the
implementation needs to define it, and if they define it, they need to
allow it.

Ben Bacarisse

unread,
Jul 31, 2022, 4:35:41 PM7/31/22
to
Yes, we've both said this more than once. I wonder if there is any
disagreement...

--
Ben.

Keith Thompson

unread,
Jul 31, 2022, 7:48:40 PM7/31/22
to
I disagree.

C++ added nullptr in the 2011 standard. It's a keyword that
is a null pointer constant and *only* a null pointer constant,
not requiring any special case rules about converting integer
expressions.

In addition, C++ narrowed the definition of "null pointer
constant", so it can be an integer literal (not a more general
integer constant expression, and not cast to void*) or a prvalue
of type std::nullptr_t.

C++ still defines NULL as an implementation-defined null pointer
constant in <cstddef>. That's necessary to avoid breaking existing
code.

But if you ignore all that and just use nullptr when you want a
null pointer constant, the result is IMHO much cleaner. I'm glad
to see C adopting a similar solution.

Keith Thompson

unread,
Jul 31, 2022, 7:55:47 PM7/31/22
to
Richard Damon <Ric...@Damon-Family.org> writes:
> On 7/31/22 5:12 AM, David Brown wrote:
>> It is also not uncommon in microcontrollers for address zero to be a
>> useful address - and then it is user code that needs access.  But it
>> is rarely an issue in practice.  The most common situation is having
>> it as part of the flash for code, and it is typically part of the
>> interrupt vectors or reset vector.  You would only want to read it
>> for something like a CRC check of the flash, and if you are
>> concerned about the compiler handling a pointer to address zero in
>> an unhelpful manner, you can use a pointer-to-volatile.  The other
>> cases I have seen are having memory mapped peripherals there - and
>> again, you always use volatile accesses.
>
> And for machine specific case like this, the fact that derefencing a
> NULL pointer is "just" Undefined Behavior, that can be Implementaton
> Defined to do what is wanted, as opposed to some how PROHIBITED.
>
> *(char *0) = 0;
>
> Is a fully legal statement that must be translated.

No, it can be rejected at compile time. See the definition of
"undefined behavior":

NOTE Possible undefined behavior ranges from ignoring the
situation completely with unpredictable results, to behaving
during translation or program execution in a documented manner
characteristic of the environment (with or without the issuance
of a diagnostic message), to terminating a translation or
execution (with the issuance of a diagnostic message).

> Yes, when you execute it, you get undefined behavior, but that might
> be just simply defined to write to location 0.

Certainly an implementation *can* do that, and it's what I'd want and
expect on an implementation where location 0 is usable.

Having location 0 be usable doesn't preclude using all-bits-zero to
represent a null pointer. It means you can't distinguish between a
valid pointer to location 0 and a null pointer, but in practice that's
not likely to be a huge problem; it just requires some care by the
programmer. Similarly, (time_t)-1 is an error value when returned by
the time() function, but it can also be a valid time, typically
1969-12-31 23:59:59 UTC.

Keith Thompson

unread,
Jul 31, 2022, 7:57:33 PM7/31/22
to
Ben Bacarisse <ben.u...@bsb.me.uk> writes:
[...]
> I'm not sure why you think this is a problem. Sure, you can't pull any
> value you like out of the air and assume that you can access it, but so
> what? Any valid void * can be converted to, say, uintptr_t and back
> again, so there /are/ integers that map to pointers in every conforming
> implementation.
[...]

A conforming implementation might not define [u]intptr_t. It still must
support conversions between pointers and integers, but the results of
such conversions might not be meaningful (other than the special case of
a null pointer constant).

Richard Damon

unread,
Jul 31, 2022, 8:09:42 PM7/31/22
to
But in this case, the Undefined Behavior occurs only at execution time,
so unless the compiler can show that the statement MUST be executed (and
maybe before executiong observable behavior, I forget it UB is allowed
to time travel), it can't do anything at compile time.

A file scope decleration like

int bomb = *(int*)0;

might allow a compile time rejection for undefined behavior.

The Termination of Translation can ONLY apply to UB that has occured at
translation time or possibly for execution time behavior that MUST
happen, as otherwise the implementation have violated the requirements,
since the UB hasn't happened yet, and might not ever happen.

Malcolm McLean

unread,
Aug 1, 2022, 1:43:04 AM8/1/22
to
The question is whether NULL is really a language feature, or just a
construct that programmers tend to find useful.
In original C, char *ptr = 0x1234; would put address 1234 hex in ptr,
unproblematically. So char *ptr = 0; wasn't special. Nowadays, C has
been ported to widely different architectures, and absolute addresses
in notionally portable code cannot be supported. So we have a special
rule for the null pointer.
Also, because historically null has been all bits zero, an if statement
is defined to evaluate false if passed a null pointer.

But are those tiny areas of explicit language support enough to say that,
therefore, null deserves its own keyword? The whole philosophy of C
is that it is minimal. Everything that can be done by C code rather than
by the compiler is done by C code. The exception, floating point operations
on systems without floating point hardware, is very much a special case.

Keith Thompson

unread,
Aug 1, 2022, 2:25:20 AM8/1/22
to
What's the difference?

> In original C, char *ptr = 0x1234; would put address 1234 hex in ptr,
> unproblematically. So char *ptr = 0; wasn't special.

`char *ptr = 0x1234; has been a constraint violation since C89.

> Nowadays, C has
> been ported to widely different architectures, and absolute addresses
> in notionally portable code cannot be supported. So we have a special
> rule for the null pointer.
> Also, because historically null has been all bits zero, an if statement
> is defined to evaluate false if passed a null pointer.

Historically, perhaps, but since at least 1989 a null pointer value is
treated as false regardless of how it's represented.

> But are those tiny areas of explicit language support enough to say that,
> therefore, null deserves its own keyword? The whole philosophy of C
> is that it is minimal. Everything that can be done by C code rather than
> by the compiler is done by C code. The exception, floating point operations
> on systems without floating point hardware, is very much a special case.

That's not the "whole philosohy" of C.

Yes, in my opinion the null pointer deserves its own keyword.
So do true and fase (which are keywords in C23).

If you don't like them, you don't have to use them.

Malcolm McLean

unread,
Aug 1, 2022, 3:10:37 AM8/1/22
to
On Monday, 1 August 2022 at 07:25:20 UTC+1, Keith Thompson wrote:
> Malcolm McLean <malcolm.ar...@gmail.com> writes:
>
> Yes, in my opinion the null pointer deserves its own keyword.
> So do true and fase (which are keywords in C23).
>
> If you don't like them, you don't have to use them.
>
No, that's naive.

David Brown

unread,
Aug 1, 2022, 3:56:12 AM8/1/22
to
Not in this case, no.

When a new feature is added to the language core or the standard
library, you might come across it in other people's code even if you
don't use it yourself. That's always a possibility. (But the same can
be said of any construct in any language, regardless of changes - I
don't like shouty all-caps macro names, but I still have to use them in
code from elsewhere.)

I think it is probably fair to say that almost any C code that uses
identifiers "true" or "false" in a way that conflicts with having them
as keywords representing constants of type _Bool is either pathological
(such as using "sizeof(false)"), incorrect code (such as accidentally
using "false" as a null pointer), or weird legacy code from the dark
ages (such as code that #define's "true" as -1).

Exceptions will be pretty much negligible.

The only place I could imagine getting trouble would be code that
defines "true" and "false" itself, rather than using <stdbool.h>.
Hopefully such legacy code will be relatively rare now, but it is
clearly a possible issue.


The likelihood of the identifier "nullptr" being used for anything other
than a null pointer (usually for compatibility with C++) is almost
non-existent, as is the likelihood of misunderstanding it when you read
it (even if you are unfamiliar with C++ or C23).

Ben Bacarisse

unread,
Aug 1, 2022, 3:18:06 PM8/1/22
to
Malcolm McLean <malcolm.ar...@gmail.com> writes:

> The question is whether NULL is really a language feature, or just a
> construct that programmers tend to find useful.
> In original C, char *ptr = 0x1234; would put address 1234 hex in ptr,
> unproblematically.

No. The conversion from integer types to pointer types required a cast
in "original C" (by which I mean K&R C). K&R C included accesses that
are not conversions (due to not having function prototypes) and they
have always been considered risky. K&R (the book) has a section
"Pointer are not integers" to explain this pitfall -- one that no longer
exists in modern C.

> So char *ptr = 0; wasn't special.

0 has always been special in regard to pointer. How special has indeed
changed over the years, but it has never been "just another number" in
relation to pointers.

<snip>
--
Ben.

Grant Mulholland

unread,
Aug 14, 2022, 1:21:11 AM8/14/22
to
On Friday, July 22, 2022 at 2:32:12 PM UTC-5, Thiago Adams wrote:
> - nullptr
> - typed enumerations
> - improved normal enumerations
> - Comma omission and comma deletion
> - constexpr
> - auto
> - TIME_MONOTONIC
> - bit-precise bit-fields
> - relaxed requirements for variadic parameter lists
> - embed
> ...
Good lord, auto in C...

Lynn McGuire

unread,
Aug 15, 2022, 10:21:17 PM8/15/22
to
I don't like it in C++ either.

Lynn


Tim Rentsch

unread,
Aug 16, 2022, 1:53:10 AM8/16/22
to
Richard Damon <Ric...@Damon-Family.org> writes:

> A file scope decleration like
>
> int bomb = *(int*)0;
>
> might allow a compile time rejection for undefined behavior.

It's a constraint violation. The program may be rejected
on that basis alone.

Tim Rentsch

unread,
Aug 16, 2022, 1:54:54 AM8/16/22
to
Sadly, it seems the disease that has turned C++ into a
wasteland has been transmitted to the people responsible
for the C standard.

Tim Rentsch

unread,
Aug 16, 2022, 2:17:48 AM8/16/22
to
Richard Damon <Ric...@Damon-Family.org> writes:

> On 7/31/22 5:12 AM, David Brown wrote:
>
>> It is also not uncommon in microcontrollers for address zero to be a
>> useful address - and then it is user code that needs access. But it
>> is rarely an issue in practice. The most common situation is having
>> it as part of the flash for code, and it is typically part of the
>> interrupt vectors or reset vector. You would only want to read it
>> for something like a CRC check of the flash, and if you are
>> concerned about the compiler handling a pointer to address zero in
>> an unhelpful manner, you can use a pointer-to-volatile. The other
>> cases I have seen are having memory mapped peripherals there - and
>> again, you always use volatile accesses.
>
> And for machine specific case like this, the fact that derefencing a
> NULL pointer is "just" Undefined Behavior, that can be Implementaton
> Defined to do what is wanted, as opposed to some how PROHIBITED.

Dereferencing a null pointer is unequivocally undefined behavior.
It is never implementation-defined behavior. Even if an
implementation defines and documents an extension that specifies
a behavior for dereferencing a null pointer, as the C standard
defines the terms such cases fall under the heading of undefined
behavior, and not implementation-defined behavior.

Tim Rentsch

unread,
Aug 16, 2022, 2:24:52 AM8/16/22
to
Richard Damon <Ric...@Damon-Family.org> writes:

[...]

> The key point is that *IF* the implementation defines how to
> access a specific location of memory, and you use that method,
> then the implementation must honor its definition.

AFAIAA, the C standard does not require that implementations
carry out their documented extensions correctly. We might like
it to be so, but I don't see anything in the C standard that
requires it.

Bonita Montero

unread,
Aug 16, 2022, 2:38:13 AM8/16/22
to
Sometimes it's simply necessary.
You can't have a statically typed lambda.
You can't know the type of an object returned by bind().

Philipp Klaus Krause

unread,
Aug 16, 2022, 2:42:50 AM8/16/22
to
Am 26.07.22 um 22:27 schrieb Kaz Kylheku:
>
> (at least if we momentarily ignore the
> squabbling about whether any memset call can actually be secure):

You should use C23 memset_excplicit here.

David Brown

unread,
Aug 16, 2022, 3:21:55 AM8/16/22
to
The only constraint I see for the indirection operator is in 6.5.3.2p2:

"""
The operand of the unary * operator shall have pointer type."
"""

(The constraints for casting are not violated either, as far as I can see.)


The semantics for indirection say "If an invalid value has been assigned
to the pointer, the behaviour of the unary * operator is undefined". A
null pointer is mentioned in a footnote as an example of an invalid
pointer, but it is not a constraint violation.

Which constraint do you think the code line violates?

Philipp Klaus Krause

unread,
Aug 16, 2022, 3:32:28 AM8/16/22
to
Am 16.08.22 um 09:21 schrieb David Brown:
> Which constraint do you think the code line violates?

6.7.10: "All the expressions in an initializer for an object that has
static or thread storage duration or is declared with the constexpr
storage-class specifier shall be constant expressions or string literals."

David Brown

unread,
Aug 16, 2022, 5:06:45 AM8/16/22
to
Ah, it is at file scope (and therefore static storage duration). I
missed that. Thanks.

It's easy to miss details of old threads with Tim's necroposting, but
sometimes he has some subtle or interesting information that he is
hiding, and there's a chance to learn something if it can be teased out
of him.

Richard Damon

unread,
Aug 17, 2022, 8:11:35 PM8/17/22
to
Note, I didn't say that it was "Implementation Defined Behavior", but
Undefined Behavior that the implementation can define.

The implementation can define ANYTHING it wants (as an extension), as
long as it doesn't violate a requirement of the Standard, and the
Standard explicitly provides no requirement on things it declares to be
Undefined Behavior, so an Implementation is free to define that behavior
as anything.

Keith Thompson

unread,
Aug 17, 2022, 11:09:04 PM8/17/22
to
Richard Damon <Ric...@Damon-Family.org> writes:
> On 8/16/22 2:17 AM, Tim Rentsch wrote:
>> Richard Damon <Ric...@Damon-Family.org> writes:
[...]
>>> And for machine specific case like this, the fact that derefencing a
>>> NULL pointer is "just" Undefined Behavior, that can be Implementaton
>>> Defined to do what is wanted, as opposed to some how PROHIBITED.
>> Dereferencing a null pointer is unequivocally undefined behavior.
>> It is never implementation-defined behavior. Even if an
>> implementation defines and documents an extension that specifies
>> a behavior for dereferencing a null pointer, as the C standard
>> defines the terms such cases fall under the heading of undefined
>> behavior, and not implementation-defined behavior.
>
> Note, I didn't say that it was "Implementation Defined Behavior", but
> Undefined Behavior that the implementation can define.

You use the phrase "Implementation Defined", which is close enough to
the standard-defined term "implementation-defined behavior" (the
standard also defines "implementation-defined value") that it's almost
guaranteed to cause confusion.

I presume you know what "implementation-defined behavior" means, but the
above could easily have been written by someone who incorrectly thinks
it means "behavior that is defined by the implementation".

> The implementation can define ANYTHING it wants (as an extension), as
> long as it doesn't violate a requirement of the Standard, and the
> Standard explicitly provides no requirement on things it declares to
> be Undefined Behavior, so an Implementation is free to define that
> behavior as anything.

Agreed.

Richard Damon

unread,
Aug 17, 2022, 11:19:39 PM8/17/22
to
Yes, unfortunately, the language of the Standard doesn't give a good
term for things the Implementation chooses to define but wasn't required
to define.

Kaz Kylheku

unread,
Aug 18, 2022, 3:04:01 AM8/18/22
to
Isn't the above-discussed word "extension" it?

"A conforming implementation may have extensions (including additional
library functions), provided they do not alter the behavior of any
strictly conforming program".

and all that.

A predictable, catchable SIGSEGV signal being generated upon a null
pointer dereference (or at least one not sufficiently displaced away
from null), is an example of an extension. Does it not suffice?

--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal

Andrey Tarasevich

unread,
Aug 18, 2022, 3:03:41 PM8/18/22
to
It is just another message to those who insist on explicitly casting the
result of `malloc`, use types under `sizeof` or keep scattering
unnecessary references to type names in their code in many other ways,
because it allegedly "helps them to remember which types they are
working with". It is clear that C and C++ are gravitating towards
type-agnostic programming paradigm. Specific type names belong in
declarations and nowhere else. "Remembering which types you are working
with" is a crutch, and it is time to learn walking without crutches. Try
it, it feels scary at first, but it is much more efficient.

--
Best regards,
Andrey

Chris M. Thomasson

unread,
Aug 18, 2022, 3:57:15 PM8/18/22
to
A C programmer who refuses to try C++, ever... A C++ diehard says: Try
C++, or you will get to experience the more "stringent" whipping implement:

https://youtu.be/BdkgV3MFkr0?t=28

;^)

William Ahern

unread,
Aug 18, 2022, 7:15:30 PM8/18/22
to
Andrey Tarasevich <andreyta...@hotmail.com> wrote:
> On 8/13/2022 10:21 PM, Grant Mulholland wrote:
>> On Friday, July 22, 2022 at 2:32:12 PM UTC-5, Thiago Adams wrote:
>>> - nullptr
>>> - typed enumerations
>>> - improved normal enumerations
>>> - Comma omission and comma deletion
>>> - constexpr
>>> - auto
>>> - TIME_MONOTONIC
>>> - bit-precise bit-fields
>>> - relaxed requirements for variadic parameter lists
>>> - embed
>>> ...
>> Good lord, auto in C...
>
> It is just another message to those who insist on explicitly casting the
> result of `malloc`, use types under `sizeof` or keep scattering
> unnecessary references to type names in their code in many other ways,
> because it allegedly "helps them to remember which types they are
> working with".

auto wouldn't work with malloc. Relatedly, typeof is missing from the list;
it's in the working draft, N3047, though.

Thiago Adams

unread,
Aug 18, 2022, 9:48:04 PM8/18/22
to
like this?

auto p = (struct X*)malloc(sizeof(struct X));

this list has only the last additions.






Andrey Tarasevich

unread,
Aug 18, 2022, 11:24:08 PM8/18/22
to
On 8/18/2022 6:47 PM, Thiago Adams wrote:
>
>> auto wouldn't work with malloc.
> like this?
>
> auto p = (struct X*)malloc(sizeof(struct X));
>
> this list has only the last additions.
>

You don't need `auto` with `malloc`, since the idiomatic pattern for the
typical usage of `malloc` looks as follows

ptr = malloc(sizeof *ptr);
array = malloc(N * sizeof *array);

It is already sufficiently type-agnostic anyway.

--
Best regards,
Andrey

Tim Rentsch

unread,
Aug 23, 2022, 12:43:37 PM8/23/22
to
The C standard frequently uses the adjectival phrase "implementation
defined" (normally with a hyphen) to mean implementation-defined
behavior. I understand the point you're trying to make. My complaint
is not with the point but with the language you are using to express
it.

> The implementation can define ANYTHING it wants (as an extension), as
> long as it doesn't violate a requirement of the Standard, and the
> Standard explicitly provides no requirement on things it declares to
> be Undefined Behavior, so an Implementation is free to define that
> behavior as anything.

Any construct that an implementation compiles, it defines. The behavior
can be defined explicitly by documenting an extension, or it can be
defined implicitly by what the generated code actually does. Either
way, the implementation defines the behavior, but the point you are
trying to make is not that.

> [from a later posting]
> Yes, unfortunately, the language of the Standard doesn't give a good
> term for things the Implementation chooses to define but wasn't
> required to define.

Yes, it does. An implementation can document an extension that
specifies the behavior of the construct in question.

Keith Thompson

unread,
Aug 23, 2022, 2:02:44 PM8/23/22
to
Tim Rentsch <tr.1...@z991.linuxsc.com> writes:
> Richard Damon <Ric...@Damon-Family.org> writes:
[...]
>> The implementation can define ANYTHING it wants (as an extension), as
>> long as it doesn't violate a requirement of the Standard, and the
>> Standard explicitly provides no requirement on things it declares to
>> be Undefined Behavior, so an Implementation is free to define that
>> behavior as anything.
>
> Any construct that an implementation compiles, it defines. The behavior
> can be defined explicitly by documenting an extension, or it can be
> defined implicitly by what the generated code actually does. Either
> way, the implementation defines the behavior, but the point you are
> trying to make is not that.

You can put it that way if you like, but the standard uses the
term "implementation-defined" specifically to refer to things that must
be documented, and "unspecified" to refer to things that may or may not
have to be documented.

For unspecified and not implementation-defined behavior, the choice of
actual behavior might not even be deliberate.

>> [from a later posting]
>> Yes, unfortunately, the language of the Standard doesn't give a good
>> term for things the Implementation chooses to define but wasn't
>> required to define.
>
> Yes, it does. An implementation can document an extension that
> specifies the behavior of the construct in question.

An implementation might, for example, document the order of evaluation
of the operands to "+". I don't think that would be considered an
extension (though the standard doesn't define the term "extension"
precisely).

Tim Rentsch

unread,
Aug 23, 2022, 4:41:03 PM8/23/22
to
Keith Thompson <Keith.S.T...@gmail.com> writes:

> Tim Rentsch <tr.1...@z991.linuxsc.com> writes:
>
>> Richard Damon <Ric...@Damon-Family.org> writes:
>
> [...]
>
>>> The implementation can define ANYTHING it wants (as an extension), as
>>> long as it doesn't violate a requirement of the Standard, and the
>>> Standard explicitly provides no requirement on things it declares to
>>> be Undefined Behavior, so an Implementation is free to define that
>>> behavior as anything.
>>
>> Any construct that an implementation compiles, it defines. The behavior
>> can be defined explicitly by documenting an extension, or it can be
>> defined implicitly by what the generated code actually does. Either
>> way, the implementation defines the behavior, but the point you are
>> trying to make is not that.
>
> You can put it that way if you like, but the standard uses the
> term "implementation-defined" specifically to refer to things that must
> be documented, and "unspecified" to refer to things that may or may not
> have to be documented.

Yes, the C standard uses the terms "implementation-defined behavior"
and "unspecified behavior" with very specific meanings, along with
the corresponding shortenings "implementation-defined" and
"unspecified". My point is to distinguish the usage of words
"define" and "document" as they occur in ordinary English, and also
to avoid using phrases that might be confused with how similar
phrases are used in the C standard.

> For unspecified and not implementation-defined behavior, the
> choice of actual behavior might not even be deliberate.

Sure. What behavior results might be an accidental, unconscious,
unaware, unknowing, or even unexpected and unintended consequence of
how the compiler is written. It is still the case that the compiler
implicitly defines the behavior, by virtue of what choices are made
to produce the compiled code.

>>> [from a later posting]
>>> Yes, unfortunately, the language of the Standard doesn't give a good
>>> term for things the Implementation chooses to define but wasn't
>>> required to define.
>>
>> Yes, it does. An implementation can document an extension that
>> specifies the behavior of the construct in question.
>
> An implementation might, for example, document the order of evaluation
> of the operands to "+". I don't think that would be considered an
> extension (though the standard doesn't define the term "extension"
> precisely).

To my way of thinking any specification of behavior that goes beyond
what the C standard mandates counts as an extension (provided of
course it is documented), regardless of whether the additional
semantics falls into the realm of unspecified behavior, undefined
behavior, or even contradicts what the C standard says (assuming of
course the change has no effect on any strictly conforming program).
If the implementation wants to say that some additional semantic
rules are in effect then that needs to be documented somewhere, and
the only kinds of such documentation mentioned in the C standard are
for implementation-defined behavior, and for extensions.
0 new messages