Integer representation of a NULL constant: guaranteed to be unique?

This is "according to the very words of the C-Standard" -Question. Is it guaranteed that a cast from NULL to an unsigned integer large enough to hold any pointer results in the same value, regardless of the type of the pointer that contains NULL?

See the following code snippet where T1, T2 can be any type constructive in C:

#include <stddef.h>

/* unsigned int be known to be big enough to hold any object pointer */
unsigned int ui_ptr1, ui_ptr2, ui_ptr3, res;

ui_ptr1 = (unsigned int) NULL;
ui_ptr2 = (unsigned int) (T1 *) NULL;
ui_ptr3 = (unsigned int) (T2 *) NULL;

res = (ui_ptr1 == ui_ptr2) && (ui_ptr1 == ui_ptr3);

      

Is it res

guaranteed 1

for any T1 and T2? From ISO: 9899: 1990 (C90) I know that NULL is equivalent to a null implementation pointer constant (7.1.6). And I know that the conversion forms the given pointer type and back is well defined, but the value is implementation defined. (6.3.4, G.3.7, 2. "-").

But I cannot find any reasoning for the above. Did I miss something? Are there more guarantees in more ... recent ... C Standard iterations?

Background: I want to pass a pointer between functions through an unsigned integer (I have no choice here) through some static variable that must be initialized with the appropriate instance 3 .. The initializing instance does not know the type of the passed pointer, so it needs to be initialized with an integer constant (unchanged) which then represents the / correct NULL-Pointer constant. If undefined behavior is hiding when I try to do something about it. (see also: Allowed Operations with Possibly Invalid Pointer by Strict Interpretation of the C Standard ).

+3


source to share


1 answer


From C99 and C11, 6.3.2.3 p3:

An integer constant expression with a value of 0, or such an expression passed to the void * type, is called a null pointer constant. 55) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unevenly with a pointer to any object or function.

And p4:

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

(Additionally, note 56/67 notes that "The mapping functions for converting a pointer to an integer or integer number to a pointer are intended to be consistent with the addressing structure of the runtime.")

(The C89 project has the equivalent text for C99 shown in section 3.2.2.3 above).

So it seems pretty safe to assume that in an architecture with a linear addressing scheme and where the pointer size is equal to the word size, what one integer value represents NULL

- especially if you go through the void *

type. In theory, however, a pointer NULL

(even void *

) when converted to an integer may not equal 0, since this is not necessary. You can get around this by always converting through void *

before comparing the value NULL

.



That is, if you change the above ui_ptr2 and ui_ptr3 declarations to:

ui_ptr2 = (unsigned int) (void *) (T1 *) NULL;
ui_ptr3 = (unsigned int) (void *) (T2 *) NULL;

      

... then you can at least make sure that each of them:

(void *) ui_ptr2 == (void *) ui_ptr3
(void *) ui_ptr2 == NULL
(void *) ui_ptr3 == NULL

      

... will be true. If you added either (void *) ui_ptr2

or (void *) ui_ptr3

to another pointer type, it should still be NULL

in line with 6.3.2.3 p4 above.

However, you are relying on the pointer-to-integer conversion to be reversible. A conversion is an implementation defined both ways, and while it is hoped that converting a pointer to an integer and then back to a pointer will yield the original pointer value, which is not strictly defined (although the above footnote 56 strongly recommends that it be implied). , there is no guarantee that all possible pointer values ​​will be represented in any integer type (6.3.2.3 p6). Your only real way is to check what sizeof(int) >= sizeof(void *)

, and then use memcpy

to pass the representation of the pointer to and from an integer, in which case you no longer need to traverse through void *

(just if the type of the pointer you are storing is the same as the type you are retrieving).

+3


source







All Articles