The answer to this, which I see to what extent the NULL value can be dereferenced, is whether it is intentionally left platform-dependent indefinitely, due to the fact that it remains to be implemented in C11 6.3.2.3p5 and p6. This is mainly to support standalone implementations used to develop boot code for the platform, as the OP points out in its rebuttal link, but also has applications for the hosted implementation.
Re:
(C11, 6.5.3.2p4) "If an invalid value is assigned to the pointer, the behavior of the unary * operator is undefined.102)"
102): "Among the invalid values for dereferencing a pointer by a unary operator," * "is a null pointer, an address inadequately aligned with the type of object that it points to, and the address of the object after its end."
This is expressed as it is, afaict, because each of the cases in the footnote CANNOT be invalid for certain platforms that the compiler targets. If there is a defect, this “invalid value” should be italicized and defined “using implementation”. For the alignment case, the platform can have access to any type using any address, so there is no alignment requirement, especially if address translation is supported; and the platform can assume that the lifetime of the object ends only after the application exits, allocating a new frame via malloc () for automatic variables with every function call.
For null pointers during loading, the platform may have expectations that the structures used by the processor have certain physical addresses, including at address 0, and are obtained as object pointers in the source code or may require a function that determines the loading process, use base address 0. If the standard did not allow views such as & podhd-> line6, where the platform requires podhd to have a base address of 0, then assembly language is required to access this structure. Similarly, for the soft reset function, you may need to dereference a 0-digit pointer as a call to the void function. The hosting implementation can consider 0 the base of the executable image and map the NULL pointer in the source code to the header of this image after loading, since the structure must have a logical address 0 for this instance of virtual machine C.
The fact that standard call pointers are more processed in the virtual address space of the virtual machine, where object handlers have more requirements for what operations are allowed for them. How the compiler emits code that takes into account the requirements of these handles for a particular processor remains undefined. What is effective for one processor may not be for another, after all.
The requirement for (void *) 0 is greater than the compiler emitting code that guarantees the expressions in which the source uses (void *) 0, explicitly or by referencing NULL, that the actual stored value will be what it could mean, t points to any valid function definitions or objects using any conversion code. It should not be 0! Similarly, for casts (void *) from 0 to (obj_type) and (func_type), they are only needed to get assigned values, which are evaluated as addresses, the compiler guarantees are not used, and then for objects or code. The difference with the latter is that they are not used, and are not invalid, so they can be dereferenced in a certain way.
Then the code that checks for pointer equality checks if one of the operands is one of these values, the other is one of three, and not just the same bit pattern, because it drags them with RTTI like (null *), great from pointer types void, obj and func for certain objects. The standard may be more explicit, it is a separate type if it is not specified, because compilers use it only internally, but I assume that this is considered to be the explicit "null pointer" path in italics. Effectively, imo, a '0' in these contexts is an additional compiler keyword due to the additional requirement of its type identification (null *), but is not characterized as such because it will complicate the definition of <; identifiers>.
This stored value can be SIZE_MAX as easy as 0 for (void *) 0 in the emitted application code, when implementations, for example, define the range 0 to SIZE_MAX-4 * sizeof (void *) of the virtual machine treats as what really for code and data. The NULL macro can even be defined as: (void *) SIZE_MAX, and for the compiler it would have to find out from the context that it has the same semantics as 0. The coding code is responsible for the fact that this selected value, in pointer-pointers - pointers and the delivery of what is suitable as a pointer to an object or function. integer pointer throws, implicit or explicit, have similar validation and delivery requirements; especially in unions where the field (u) intptr_t overlaps the field (type *). Portable code can protect against compilers without doing it right, with an explicit expression * (ptr == NULL? (Type *) 0: ptr).