I know C compilers aren't required to use all zeros for the bit representation of NULL
, but they *are* required by the standard to make NULL
evaluate to false in boolean contexts/comparisons. Hence the 2nd printf
in the program below will always output false
.
But what I want to know is: on systems where NULL
is *not* all zeros, will a pointer value that *is* all zeros also evaluate to false in boolean contexts/comparisons? In other words, will the 1st printf
in the program below ever output true
?
Or asked in a slightly different way: can I rely on calloc
to produce a pointer value that will always evaluate to false in boolean contexts/comparisons? The 1st answer to this question uses memset
to clear the bits of a long*
named y
, then goes on to say that y==0
is UB because y
may be a "trap representation" (whatever that is). calloc
is also just clearing bits, so maybe o->p
in the 1st printf
is also UB?
#include <stdio.h> #include <stdlib.h> #include <assert.h> typedef struct { void * p; } obj; int main() { obj * o = calloc(sizeof(obj), 1); assert(o); // assume successful allocation printf("%s\n", o->p ? "true" : "false"); // 1st: could print "true"? Is o->p UB? o->p = NULL; printf("%s\n", o->p ? "true" : "false"); // 2nd: always prints "false" return 0; }
The null pointer constant is always 0. The NULL macro may be defined by the implementation as a naked 0 , or a cast expression like (void *) 0 , or some other zero-valued integer expression (hence the "implementation defined" language in the standard).
A null pointer constant is an integer constant expression that evaluates to zero. For example, a null pointer constant can be 0, 0L , or such an expression that can be cast to type (void *)0 .
It's not even an address abstraction, it's the constant specified by the C standard and the compiler can translate it to some other number as long as it makes sure it never equals a "real" address, and equals other null pointers if 0 is not the best value to use for the platform.
Null pointer is a pointer which points nothing. Some uses of null pointer are: b) To initialize a pointer variable when that pointer variable isn't assigned any valid memory address yet. b) To pass a null pointer to a function argument when we don't want to pass any valid memory address.
typedef struct { void * p; } obj; obj * o = calloc(sizeof(obj), 1); assert(o); // Let us set aside the case of a failed allocation printf("%s\n", o->p ? "true" : "false"); // 1st: could print "true" ?
can I rely on
calloc
to produce a pointer value that will always evaluate to false in boolean contexts/comparisons?
No - output could be "true"
.*1.
The bit pattern of all zeros, as a pointer, may not be a null pointer.
7.22.3.2 The calloc function
2 The calloc function allocates space for an array ofnmemb
objects, each of whose size is size. The space is initialized to all bits zero.301)
Footnote 301) Note that this need not be the same as the representation of floating-point zero or a null pointer constant.
Example: An implementation may only have only a single null pointer encoding with a bit pattern of all ones. (void *)0
converts the all zeros bit pattern int 0
to an all ones void *
. if (null_pointer)
is always false, regardless of the bit pattern of the null pointer.
*1 Yet practically yes, output is always "false"
. Implementations are uncommon these days that do not use all zero bit pattern as a null pointer. Highly portable code would not assume this practicality. Consider an old or new novel system may use a zero bit pattern as a non-null pointer - and sadly break many a code base that assumes an all zero bit pattern is a null pointer.
Consider the following places where the logical value of an expression is used, all taken from C18, my emphasis in bold italic:
6.3.1.2 (Boolean type) p1: When any scalar value is converted to _Bool
, the result is 0 if the value compares equal to 0; otherwise, the result is 1.
6.5.3.3 (Unary arithmetic operators) p5: The result of the logical negation operator !
is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int
. The expression !E
is equivalent to (0==E)
.
6.5.13 (Logical AND operator) p3: The &&
operator shall yield 1 if both of its operands compare unequal to 0; otherwise, it yields 0. The result has type int
.
6.5.14 (Logical OR operator) p3: The ||
operator shall yield 1 if either of its operands compare unequal to 0; otherwise, it yields 0. The result has type int
.
6.5.15 (Condtional operator) p4: The first operand is evaluated; there is a sequence point between its evaluation and the evaluation of the second or third operand (whichever is evaluated). The second operand is evaluated only if the first compares unequal to 0; the third operand is evaluated only if the first compares equal to 0; the result is the value of the second or third operand (whichever is evaluated), converted to the type described below.
6.8.4.1 (The if
statement) p2: In both forms, the first substatement is executed if the expression compares unequal to 0. In the else
form, the second substatement is executed if the expression compares equal to 0. If the first substatement is reached via a label, the second substatement is not executed.
6.8.5 (Iteration statements) p4: An iteration statement causes a statement called the loop body to be executed repeatedly until the controlling expression compares equal to 0. The repetition occurs regardless of whether the loop body is entered from the iteration statement or by a jump.
"E compares equal to 0" is equivalent to the C expression (E == 0)
, and "E compares unequal to 0" is equivalent to the C expression (E != 0)
. The constraints of the equality operators are given by:
void
; orRegarding the semantics of the equality operators where at least one operand is a pointer:
6.5.9 (Equality operators) p5: Otherwise, at least one operand is a pointer. If one operand is a pointer and the other is a null pointer constant, the null pointer constant is converted to the type of the pointer. If one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void
, the former is converted to the type of the latter.
p6: Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.
Regarding null pointer constants:
void *
, is called a null pointer constant67). If a null pointer constant 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.But what I want to know is: on systems where
NULL
is not all zeros, will a pointer value that is all zeros also evaluate to false in boolean contexts/comparisons?
Aside: NULL
is a null pointer constant, not necessarily a null pointer (see 6.3.2.3p3 above where it could be an integer constant expression). What you really mean is a system where the bit representation of a null pointer is not all zeros.
Note: As pointed out by Eric Postpischil in the comments below, a system could have several bit representations of null pointer values, so we assume that none of them are all-zero bit representations for this question.
In order for the pointer value to evaluate to false in boolean contexts/comparisons, it must compare unequal to 0. In this context, it must compare unequal to a null pointer constant. By 6.5.9p5 above, the null pointer constant will be converted to the type of the pointer it is being compared to. By 6.5.9p6 above, a null pointer value will not compare equal to a non-null pointer value. So a non-null pointer value with all bits zero on a system where a null pointer value is not all bits zero will evaluate to true in a boolean context.
Or asked in a slightly different way: can I rely on
calloc
to produce a pointer value that will always evaluate to false in boolean contexts/comparisons?
No, you cannot rely on calloc
(or memset
with byte value 0) to produce a pointer value that will evaluate to false in boolean contexts. If a pointer value with an all-zero bit representation is not a null pointer value it will evaluate to true in boolean contexts.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With