7
Consider this union:
typedef union
{
void* vptr;
nullptr_t nptr;
} pun_intended;
nullptr_t
is supposedly compatible with void*
. Ok so what if we initialize the void*
to some non-zero value?
pun_intended foo = { .vptr = (void*)42 };
- This conversion is supposedly legit (impl.defined) as per C23 6.3.2.3 §4, or at least it was until
nullptr_t
was introduced. - And what about union type punning? Also supposedly legit.
- And what about inspecting any type’s internal representation in C using a character type pointer, well-defined until C23, 6.3.2.3 §7.
Full example:
#include <stdio.h>
#include <inttypes.h>
#include <stddef.h>
typedef union
{
void* vptr;
nullptr_t nptr;
} pun_intended;
int main(void)
{
pun_intended foo = { .vptr = (void*)42 };
printf("Value: %" PRIuPTR "n", (uintptr_t)foo.vptr);
if(foo.nptr != (void*)42)
{
puts("It does not have value 42.");
if(foo.nptr == nullptr)
puts("Because it's a nullptr.");
else
puts("But it's not a nullptr.");
unsigned int val = *(unsigned char*)&foo; // little endian assumption here
printf("And it has value %d.n", val);
if(foo.vptr != nullptr)
{
puts("foo.vptr is however not a nullptr.");
}
}
}
Output on clang 16 -std=c2x:
Value: 42
It does not have value 42
Because it's a nullptr
And it has value 42.
foo.vptr is however not a nullptr
Output on gcc 13.2 -std=c2x:
Value: 42
It does not have value 42.
But it's not a nullptr.
And it has value 42.
foo.vptr is however not a nullptr.
My question: Is anything of the above (which was previously well-defined or impl.defined) now undefined/unspecied behavior? If so, where is that stated? Or are these scenarios simply not considered in C23 – a defect?
6
2 Answers
Reset to default
4
Ok so what if we initialize the
void*
to some non-zero value?
C 2023 N3096 7.21.2 3 explicitly answers this. After telling us that the representation of a nullptr
value in the nullptr_t
type is the same as for a null pointer value in the void *
type, it tells us what happens if there is a different sequence of byte values in a nullptr_t
object:
… if the object representation is different, the behavior is undefined.
1
-
1
ugh. Just what C needs: syntactic sugar that adds the wholly-new concept of an immutable type to C and opens the door to weird corner-case UB and bugs. Straight from the too-complex-to-understand, must-be-everything-to-everybody font of craziness that is C++.
– Andrew Henle7 hours ago
0
IMO it is UB:
6.3.2.3
If a null pointer constant or a value of the type nullptr_t (which
is necessarily the valuenullptr
)…
6.5.4.4
A pointer type shall not be converted to any floating type. A floating
type shall not be converted to any pointer type. The type nullptr_t
shall not be converted to any type other than void , bool or a pointer
type. No type other thannullptr_t
shall be converted tonullptr_t
Here you convert void *
to nullptr_t
via union which is explicitly prohibited.
You can convert nullptr_t
to void *
via this union but not void *
to nullptr_t
IMO nullptr_t
exists only to introduce nullptr
constant and I do not see any practical use of this type.
3
-
Although… if I can convert from nullptr_t to void*, and void* is explicitly a type through which any other object pointer conversion is possible, I should also be able to convert from void* to nullptr_t. Or otherwise nullptr_t also breaks void pointers as a generic type, in addition to all other oddities shown in the question.
– Lundin7 hours ago
-
@Lundin
No type other than nullptr_t shall be converted to nullptr_t
sounds quite definitive. And you are right – I agree, but it does not change the working used. BTWnullptr_t
exists only to introducenullptr
an I do not see any practical use of this type– 0___________7 hours ago
-
@Lundin: Re “Or otherwise nullptr_t also breaks void pointers as a generic type”: It does not break pointers because
nullptr_t
is not a pointer type. Pointer types are derived types; they are derived from object or function types per C 2023 N3096 6.2.525.nullptr_t
is a new type. Even if it were a pointer type, it would not break the rules about conversions between pointers to object types because it would not be a pointer to an object type.– Eric Postpischil7 hours ago
Not the answer you're looking for? Browse other questions tagged
or ask your own question.
or ask your own question.
nullptr_t
is different from all pointer or arithmetic types … and has exactly one valuenullptr
. Can this be squared with anullptr_t
holding the value 42?7 hours ago
@adabsurdum I think the bottom line is that no value "nullptr" exists in the real world where computers live. They have to name the exact representation of it, such as "a pointer of size
sizeof(void*)
with all bytes set to zero". Otherwise I don't think this type is any improvement over NULL, quite the contrary as it seems to make C less suitable for hardware-related programming.7 hours ago
Similarly if a
nullptr_t
must have representationnullptr
, then what exactly is "notnullptr
"? A trap representation?7 hours ago
From C23 7.21.2p2 regarding
nullptr_t
: "It has only a very limited use in contexts where this type is needed to distinguishnullptr
from other expression types", so it's probably not intended to be used in this way.7 hours ago
Re “
nullptr_t
is supposedly compatible withvoid*
”: Who supposes that and why? “Compatible” has a specific meaning in C. Largely, it means that two types can be completed to be the same type. The fact that one type can be converted to another implicitly does not mean they are compatible. The fact that two types have the same size and representation does not mean they are compatible.nullptr_t
is not compatible withvoid *
.7 hours ago
|
Show 1 more comment