2

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?

textral
  • 1,029
  • 8
  • 13

3 Answers3

3

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.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • NULL is defined as zero. If it was defined as non-zero I assume a lot of code would fail. The question is: Is the definition of NULL as zero part of some standard? – Tarik Dec 08 '21 at 13:24
  • 3
    @Tarik - the *null pointer constant* (which is what the macro `NULL` represents) is always zero-valued (6.3.2.3). As far as your source code is concerned `NULL` is zero. However, the null pointer *value* used by the underlying platform does not have to be zero - it could be `0xffffffff` or `0xdeadbeef` or something else. – John Bode Dec 08 '21 at 13:52
  • 1
    @Tarik "NULL is defined as zero."--> No. – chux - Reinstate Monica Dec 10 '21 at 23:26
  • @chux-ReinstateMonica if not then something like if(p) will fail. – Tarik Dec 11 '21 at 11:09
  • 1
    @Tarik: That is not correct because the C standard does not require the compiler to execute `if(p)` by comparing the bits representing `p` to zero. It requires `if(p)` to execute “then clause” of the `if` statement if and only if `p` is not a null pointer. The compiler can do this by comparing the bits of `p` to the bits that represent a null pointer (or, if there are multiple representations of a null pointer, by performing whatever tests are necessary to determine whether the bits of `p` are or are not a representation of a null pointer). – Eric Postpischil Dec 11 '21 at 11:12
  • @Tarik To echo Eric, even if `NULL` is defined as something other than zero, `void * p = NULL; if (p) ...` will not be true as expected. Pointers are not the same as integers. – chux - Reinstate Monica Dec 11 '21 at 14:54
1

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)

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • NULL is defined as zero. If it was defined as non-zero I assume a lot of code would fail. The question is: Is the definition of NULL as zero part of some standard? – Tarik Dec 08 '21 at 13:24
  • 2
    @Tarik Incorrect! From 7.19.3: The macros are `NULL` which expands to an ***implementation-defined*** null pointer constant. – Adrian Mole Dec 08 '21 at 13:30
  • @Tarik It's true that a lot of code would fail. In fact, so much code would fail that the C++ Standard was [unable to mandate that `NULL` now be defined as the new `nullptr`](https://stackoverflow.com/questions/32302615), even though it (IMO) obviously, obviously, obviously should have. So, yes, `NULL` must be 0, though for political, not technical reasons. This is (again IMO) a tragedy of the first water. – Steve Summit Dec 08 '21 at 13:33
  • I should have said "usually defined as zero". That said you might agree there is plenty of code like `if (!p)...` where `p` might have a value of `NULL` and the if statement assumes that NULL is zero. I assume a lot of code would fail in real life if NULL was defined as non-zero. – Tarik Dec 08 '21 at 13:36
  • 2
    @Tarik I appreciate your curiosity, but what you're asking about is not what textral is asking about, and is about to become a huge distraction if we discuss it here. You need to read [question 5.3](http://c-faq.com/null/ptrtest.html) in the [C FAQ list](http://c-faq.com/null/ptrtest.html). If you still have questions, please ask them as fresh questions, not in this comment thread. (Beware, though: I can pretty confidently predict that *any* question you ask about null pointers will be closed as a duplicate. Believe me, these questions have all been asked before. :-) ) – Steve Summit Dec 08 '21 at 13:37
1

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.

supercat
  • 77,689
  • 9
  • 166
  • 211