Is a non-constant zero integer cast to `void *` still a null pointer?
Asked Answered
O

2

6

The expresssion (void *)0 is called a null pointer. But how about the following:

int i = 0;
void *s = (void *)i;

Is s also a null-pointer? The C-language standard says:

6.3.2.3 Pointers

3 An integer constant expression with the value 0, such an expression cast to type void *, or the predefined constant nullptr is called a null pointer constant70). If a null pointer constant or a value of the type nullptr_t (which is necessarily the value nullptr) is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

4 Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might produce an indeterminate representation when stored into an object.71)

According to this s would not be a null pointer?

Ordain answered 6/6, 2024 at 4:9 Comment(4)
The C FAQ 5.18 asks "Is a run-time integral value of 0, cast to a pointer, guaranteed to be a null pointer?" and answers "No". So, it would seem I'm wrong. I'd be curious if someone identifies a current machine where it doesn't work, but apparently, in theory, the result of casting an integer variable containing zero does not need to be a null pointer. (Minor caveat: the C FAQ was written in the previous millennium — but the rules probably haven't changed.)Phonetic
@JonathanLeffler One example that has object pointers with the integral value of zero is the common Keil C(x)51 compiler for the MCU 8051 family, a widely used Harvard architecture. A zero function pointer points to the reset code. A zero generic pointer points to the DATA cell at address 0 of the internal RAM. A zero memory specific pointer points to the respective memory cell at address 0 of that memory.Hanse
@JonathanLeffler However, Keil C(x)51 is not a compliant compiler. :-D Because of its implementation, there is no chance to have a compliant NULL, except for generic pointers in principle. But to my knowledge, Keil did not take the opportunity.Hanse
@JonathanLeffler Plenty of microcontrollers have memory mapped registers at address zero (for example all of the Motorola-flavoured ones) - you'll have something like #define REGNAME (*(volatile uint8_t*)0) and that's used to access a register, not as a null pointer.Kinematograph
P
9

It will be on any typical modern system, but it doesn't have to be. Nothing in the standard requires pointer casts to behave the same for constant expressions and non-constant expressions.

The standard rationale document makes this explicit. From the C99 Rationale, Revision 5.10, 6.3.2.3:

Since pointers and integers are now considered incommensurate, the only integer value that can be safely converted to a pointer is a constant expression with the value 0. The result of converting any other integer value, including a non-constant expression with the value 0, to a pointer is implementation-defined.

Pave answered 6/6, 2024 at 5:18 Comment(0)
K
0

The expresssion (void *)0 is called a null pointer.

It is not. It is called a null pointer constant, a particular language item that may come either in the form of (void*)0 or 0 and is used for the purpose of turning an object/function pointer into a null pointer. Check out What's the difference between null pointers and NULL?

Is s also a null-pointer?

It is not, since a null pointer may only be formed from null pointer constants. The relevant part of the C standard is indeed the quoted 6.3.2.3. Note integer constant expression. (void *)i is not an integer constant expression.

The whole reason why null pointers are made something mysterious in C is because the "well-defined nowhere" could be anywhere in the memory map, highly system-dependent. The idea is that once you assign something to a null pointer constant, the resulting null pointer could in theory have any other representation than "all zeroes" internally.

For example int* ptr = 0; could in theory result in ptr containing the value 0xFFFFFFFF or something like that - the compiler should then swoop in and assign that raw representation "between the lines" whenever it detects a null pointer constant assignment.

In practice however, compilers for systems where 0 is a valid address normally don't do this, but rather use the representation of a null pointer as 0. I've had bugs like that on microcontroller systems where an accidental assignment to a null pointer caused GPIO activation. Quite dangerous, obviously.

There are supposedly some ISA that are actually designed for C in mind, using address 0 for the null pointer representation, but treating writes to that address as a software exception/trap. Or in case of virtual memory mapping, the MMU could make address 0 invalid.

Kinematograph answered 20/6, 2024 at 9:49 Comment(5)
The standard requires certain operations to produce null pointers, but it doesn't prohibit other operations from also producing null pointers. Casting non-constant integers to a pointer type is implementation-defined, and most implementations define it in such a way that casting a non-constant 0 to pointer type makes a null pointer.Pave
Also see 6.5.9p6, which mandates that a null pointer cannot compare equal to any pointer other than another null pointer. If an implementation says that (int*)0 and (int*)x compare equal, where x is 0, then (int*)x must be a null pointer on this implementation.Pave
@Pave There's nothing substantial in the C standard allowing a null pointer to get created like that. That's not implementation-defined, it's non-standard language extensions. As for (int*)0 it is not guaranteed to result in a null pointer since the standard explicitly says that 0 must be cast to a void* to result in a null pointer constant. Also as mentioned in this answer, lots of compilers do not treat null pointers as intended but just as address 0. This is a very confused part of the language overall, always was - the rationales for null pointers are all over the place.Kinematograph
0 is already a null pointer constant. You misread that section. There are 3 options for a null pointer constant: "An integer constant expression with the value 0, such an expression cast to type void *, or the predefined constant nullptr". 0 falls under the first option. Since 0 is a null pointer constant, (int*)0 must be a null pointer.Pave
@Pave Ah yeah fair enough. Anyway, I can't think of any compiler or system that would treat null pointers different than object pointers with address zero. There's probably some archaic oddball ones that make the exception.Kinematograph

© 2022 - 2025 — McMap. All rights reserved.