Is NULL data-pointer the same as NULL function-pointer?

Viewed 205

POSIX requires (I think) that function pointers can be stored in a variable of type void* and/or passed to functions expecting a void* argument, even though this is strictly non-standard.

My question is this: if I test such a variable/argument for NULL-ness, if (!(variable or argument)) say, and the result is true, does that necessarily mean that the function-pointer is NULL? Could the bit pattern for a NULL void* data-pointer ever equate to a non-NULL function-pointer value? Would any sane implementation do this? Do any common implementations do this?

EDIT: this answer (to a different question admittedly) made me wonder if I had to cast the void* intermediate back to the original function pointer type before I could test NULL-ness, otherwise it's UB... is that true? Can those who posted answers weigh in on this question?

3 Answers

C 2018 6.3.2.3 4 says:

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

This paragraph, unlike paragraph 7, does not limit the conversions to pointers to object types or pointers to function types. Therefore, if a null pointer of some pointer-to-function type is converted to void *, the result is a null pointer, and then applying ! to it yields 1.

Establishing the converse, that if applying ! to a pointer yields 1, it necessarily arose from a null pointer, it more difficult. We could imagine some non-null function pointer that, when converted to void *, yields a null pointer. Considering the intent of POSIX to allow function pointers to be temporarily stored in void *, we can conclude that converting a pointer to a function to void * should never result in a null pointer.

Could the bit pattern for a NULL void* data-pointer ever equate to a non-NULL function-pointer value?

The C standard does not discuss the bit patterns used to represent pointers. The semantics are established in terms of the values.

Would any sane implementation do this?

Certainly bare-metal boot code on some hardware might put executable instructions at address zero and call a function there for some reason and might also use address zero as a null pointer. It will simply be designed not to depend on that function at address zero not being tested for being a null pointer.

Outside of such special situations, i.e., for all practical purposes, this is not done. If some software decides it needs a special representation for a null pointer, it will set aside some address for that and not use that address for any ordinary function or object.

This C11 Draft Standard suggests that, while not part of the core standard, casting function pointers to object pointers (for purposes of testing, as in your NULL-check) falls under the category of "Common Extensions" (Annex J.5):

J.5.7 Function pointer casts

1     A pointer to an object or to void may be cast to a pointer to a function, allowing data to be invoked as a function (6.5.4).

2     A pointer to a function may be cast to a pointer to an object or to void, allowing a function to be inspected or modified (for example, by a debugger) (6.5.4)

If an implementation were to target a platform where the common idiomatic bit pattern for a null code pointer was different from the common idiomatic bit pattern for a null data pointer, it would be free to either expose the fact that the pointers are different to the programmer, or wrap pointer operations in an abstraction that would make them behave identically. The authors of the Standard made no attempt to guess which approach would be more useful, since people who were actually writing code for the platform in question would be better placed than the Committee to judge the pros and cons of each approach.

In the vastly more common situation where a target platform uses the same representation for both kinds of null pointers, the authors of the Standard would have expected that implementations would store both kinds of pointers in that fashion, but would have also regarded the idea that such implementations should behave that way as sufficiently obvious that there was no need to expend ink recommending such behavior.

The Standard's failure to mandate the common behavior does not imply any judgment that implementations should ever do anything else, but merely an acknowledgment that implementations might exist where some other behavior might sometimes be more useful than the commonplace one.

Related