Consider this union:
typedef union
{
void* vptr;
nullptr_t nptr;
} pun_intended;
nullptr_t is supposedly compatible with void* 1). 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_twas 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?
1) Source: C23 n3096 draft 7.21.2
The size and alignment of
nullptr_tis the same as for a pointer to character type. An object representation of the valuenullptris the same as the object representation of a null pointer value of typevoid*.
C 2023 N3096 7.21.2 3 explicitly answers this. After telling us that the representation of a
nullptrvalue in thenullptr_ttype is the same as for a null pointer value in thevoid *type, it tells us what happens if there is a different sequence of byte values in anullptr_tobject: