Can I trust (uintptr_t)NULL to be equal to zero?

Viewed 398

I know that NULL is not required to be an all bits zero pattern. But I do wonder if

uintptr_t x = (uintptr_t)NULL;
printf("%" PRIuPTR, x);

is guaranteed to print 0? I suspect it's not, but just want to be sure.

Or more to the point. Can I trust this?

if( ((uintptr_t)f(x) | (uintptr_t)f(y)) != 0)

Assume f is a function returning a pointer. I suspect that this piece of code heavily relies on that NULL is an all bit zero pattern. Am I correct?

I know it's considered bad practice, and I would never write something like that myself, but I wonder if it's well defined.

I stumbled upon this piece of code in this answer where the author is using

while ( ( (uintptr_t)fgets(a,100,fp1) | (uintptr_t)fgets (b,100,fp) ) != 0 ) {
        printf("%s",a);
        printf("%s",b);
}
3 Answers

In theory: no.

In practice: probably.

In the relevant standardese, the checks for null pointers are "special" in the sense that they are not numeric comparisons with the value zero, so in theory, some implementation or platform could assign a different value to null pointers.

No, there is not such a guarantee. As per 7.19 NULL is macrodefinition which expands to implementation defined null pointer constant.

But if(p) is guaranteed to be the truth if the p is not NULL (where p is a pointer).

So printf("%d\n", !!p) is guaranteed to print 0 if pointer is NULL and 1 if not

while ( fgets(a,100,fp1) || fgets (b,100,fp))

or

while ( fgets(a,100,fp1) != NULL || fgets (b,100,fp) != NULL)

or a bit more pervert way

while ( ((fgets(a,100,fp1) != NULL) | (fgets (b,100,fp) != NULL)) != 0)
while ( (!!fgets(a,100,fp1) | !!fgets (b,100,fp)) != 0)

But I do wonder if (below) is guaranteed to print 0?

uintptr_t x = (uintptr_t)NULL;
printf("%" PRIuPTR, x);

No. It commonly does, but not specified to do so.

  • (uintptr)NULL simply is not specified to convert to 0. It is fairly common practice to do so though. NULL may be an integer or a pointer. NULL may be a pointer that does not convert to an integer 0. Even if NULL is an all zeros bits pattern, it is still not specified to convert to an integer 0.

  • uintptr_t and intptr_t are optional types. They often exist, but are not required. Consider a system with void * wider than 64-bit, yet the widest integer type is 64-bit.


Can I trust this?

if( ((uintptr_t)f(x) | (uintptr_t)f(y)) != 0)

It will commonly work, but it is not highly portable.
Alternative:

if( !!f(x) | !!f(y))

I suspect that this piece of code heavily relies on that NULL is an all bit zero pattern. Am I correct?

Almost. It relies on (uintptr_t)NULL converting to 0.


In addition to conversion concerns, I find a weakness in the below code concerning fgets() returning NULL due to an input error or end-of-file.

Should fgets(a,100,fp1) return NULL due to end-of-file, the buffer is not certainly _null character terminated either (it might be stale too) - leading to more UB or old output in printing.

If an input error occurred, (wrong stream direction, file not open, etc.), then a[] is indeterminate leading to UB with printf("%s",a);

while ( ( (uintptr_t)fgets(a,100,fp1) | (uintptr_t)fgets (b,100,fp) ) != 0 ) {
    printf("%s",a);
    printf("%s",b);
}

The buffer contents of fgets() are best not read when the function returns NULL.

Related