NULL appears to be zero in my GCC test programs, but wikipedia says that NULL is only required to point to unaddressable memory.
Do any compilers make NULL non-zero? I'm curious whether if (ptr == NULL) is better practice than if (!ptr).
NULL is guaranteed to be zero, perhaps casted to (void *)1.
C99, §6.3.2.3, ¶3
An integer constant expression with the value 0, or such an expression cast to type
void *, 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 unequal
to a pointer to any object or function.
And note 55 says:
55) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant.
Notice that, because of how the rules for null pointers are formulated, the value you use to assign/compare null pointers is guaranteed to be zero, but the bit pattern actually stored inside the pointer can be any other thing (but AFAIK only few very esoteric platforms exploited this fact, and this should not be a problem anyway since to "see" the underlying bit pattern you should go into UB-land anyway).
So, as far as the standard is concerned, the two forms are equivalent (!ptr is equivalent to ptr==0 due to §6.5.3.3 ¶5, and ptr==0 is equivalent to ptr==NULL); if(!ptr) is also quite idiomatic.
That being said, I usually write explicitly if(ptr==NULL) instead of if(!ptr) to make it extra clear that I'm checking a pointer for nullity instead of some boolean value.
Notice that in C++ the void * cast cannot be present due to the stricter implicit casting rules that would make the usage of such NULL cumbersome (you would have to explicitly convert it to the compared pointer's type every time).
From the language standard:
6.3.2.3 Pointers
...
3 An integer constant expression with the value 0, or such an expression cast to type
void *, 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 unequal
to a pointer to any object or function.
...
55) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.17.
Given that language, the macro NULL should evaluate to a zero-valued expression (either an undecorated literal 0, an expression like (void *) 0, or another macro or expression that ultimately evaluates to 0). The expressions ptr == NULL and !ptr should be equivalent. The second form tends to be more idiomatic C code.
Note that the null pointer value doesn't have to be 0. The underlying implementation may use any value it wants to represent a null pointer. As far as your source code is concerned, however, a zero-valued pointer expression represents a null pointer.
In practice is the same, but NULL is different to zero. Since zero means there's a value and NULL means there isn't any. So, theoretically they are different, NULL having a different meaning and in some cases that difference should be of some use.
in practice no, !ptr is correct
Related
I included a check for nullptr in a line of C code. The compiler (gcc) complained when using -std=c17 as well as -std=gnu17.
Is there such a thing as nullptr (or equivalent) in modern C standards? (C11, C17)
If not, then why?
No, C still uses NULL for a null pointer.
C++ needs a dedicated null pointer literal because it has overloading and template type deduction. These features get confused by NULL, which expands to 0 (or something like that) in C++. However, the risk of such confusion in C is small (maybe _Generic can get confused by this), and in addition, C can use (void*)0 for a null pointer, which mitigates this risk even more.
The closest thing to C++'s nullptr is C's NULL. Which may be
an integer constant expression with the value 0,
an integer constant expression with the value 0 cast to the type void*.
A null pointer constant may be converted to any pointer type; such conversion results in the null pointer value of that type.
The formal C17 specifications state that the stddef.h header defines NULL "which expands to an implementation-defined null pointer constant." (7.19)
A null pointer constant is defined as follows (6.3.2.3)
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.) 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.
Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null
pointers shall compare equal.
Note that this makes the following program ambiguous, as NULL could be an integer constant expression (accepted by the function) or of the type void* (not accepted by the function).
#include <stdio.h>
void printInt(int n)
{
printf("%d\n", n);
}
int main(void)
{
printInt(NULL);
}
Which is why nullptr was introduced in C++11. For C, having no function overloading or type deduction, this is less of an issue.
A null pointer in C is a pointer object pointing at "null". You can turn a pointer into a null pointer by assigning it to a null pointer constant. Valid null pointer constants are 0 and (void*)0. The macro NULL is guaranteed to be a null pointer constant.
The internal representation of the pointer then becomes a "null pointer", which could in theory point at an address different from zero on some exotic system. Similarly, NULL could in theory expand to something different from zero in old, pre-standard C.
When creating C++, Bjarne Stroustrup found all of this to be needlessly complex and decided that "NULL is 0" (source: https://www.stroustrup.com/bs_faq2.html#null). Notably C++ was created long before the first standardization of C, so his arguments are less relevant to standard C than they were to pre-standard C.
For more info about null pointers vs NULL in C, see What's the difference between null pointers and NULL?
ISO C 23 now has nullptr, as well as the nullptr_t type. The proposal that introduced it has some rationale.
So, I had an argument with my professor earlier defending that NULL is not a pointer, but he kept on insisting that it is because there is such a thing as NULL pointer. So, here I am now a little bit confused if NULL is really a pointer or not
I already tried search over the internet but couldn't find any answer, so my last resort is here
In C, NULL is a macro that expands to a null pointer constant.
7.19p3
The macros are
NULL which expands to an implementation-defined null pointer constant;
...
A null pointer constant is an integer constant expression with the value 0 (
e.g., 0, 1-1, 42*0LL, etc.) or such an expression cast to (void*).
6.3.2.3p3
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant.66) 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.
Most common C implementations define NULL to be 0, 0L, or ((void*)0).
So you are correct. NULL need not be a pointer.
(IIRC, C++ doesn't even allow the (void*) cast in NULL, meaning NULL in C++ always has integer type. Because of that and because void* pointers
do not compare with regular pointers so readily in C++, C++>=11 now has a special nullptr keyword.)
NULL itself is not a pointer, it is a macro that can be used to initialize a pointer to the null pointer value of its type. When compared to a pointer, it compares equal if the pointer is a null pointer and unequal if the pointer is a valid pointer to an object of its type.
There is no semantic difference between char *p = 0; and char *p = NULL; but the latter is more explicit and using NULL instead of 0 is more informative in circumstances where the other operand is not obviously a pointer or if comparing to an integer looks like a type mismatch:
FILE *fp = fopen("myfile", "r");
if (fp == NULL) {
/* report the error */
}
Similarly, there is no semantical difference in C between '\0' and 0, they both are int constants. The first is the null byte, the second the null value. Using 0, '\0' and NULL wisely may seem futile but makes code more readable by other programmers and oneself too.
The confusion may come from misspelling or mishearing the null pointer as the NULL pointer. The C Standard was carefully proof read to only use null pointer and refer to NULL only as the macro NULL.
Note however that one the accepted definitions of NULL, #define NULL ((void*)0) makes NULL a null pointer to void.
There's nothing about the idea of a 'pointer to address 0' that's a problem.
The rule is that you're disallowed from derefencing it... it's allowed to exist, and if created will meet any criteria for "pointerhood" I can think of.
Just because it's not meaningfully a pointer to something...
Why is there a NULL in the C language? Is there a context in which just plain literal 0 would not work exactly the same?
Actually, you can use a literal 0 anyplace you would use NULL.
Section 6.3.2.3p3 of the C standard states:
An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer
constant. 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.
And section 7.19p3 states:
The macros are:
NULL
which expands to an implementation-defined null pointer constant
So 0 qualifies as a null pointer constant, as does (void *)0 and NULL. The use of NULL is preferred however as it makes it more evident to the reader that a null pointer is being used and not the integer value 0.
NULL is used to make it clear it is a pointer type.
Ideally, the C implementation would define NULL as ((void *) 0) or something equivalent, and programmers would always use NULL when they want a null pointer constant.
If this is done, then, when a programmer has, for example, an int *x and accidentally writes *x = NULL;, then the compiler can recognize that a mistake has been made, because the left side of = has type int, and the right side has type void *, and this is not a proper combination for assignment.
In contrast, if the programmer accidentally writes *x = 0; instead of x = 0;, then the compiler cannot recognize this mistake, because the left side has type int, and the right side has type int, and that is a valid combination.
Thus, when NULL is defined well and is used, mistakes are detected earlier.
In particular answer to your question “Is there a context in which just plain literal 0 would not work exactly the same?”:
In correct code, NULL and 0 may be used interchangeably as null pointer constants.
0 will function as an integer (non-pointer) constant, but NULL might not, depending on how the C implementation defines it.
For the purpose of detecting errors, NULL and 0 do not work exactly the same; using NULL with a good definition serves to help detect some mistakes that using 0 does not.
The C standard allows 0 to be used for null pointer constants for historic reasons. However, this is not beneficial except for allowing previously written code to compile in compilers using current C standards. New code should avoid using 0 as a null pointer constant.
It is for humans not compilers.
if I see in the code p = NULL; instead of p = 0; it is much easier for me to understand that p is a pointer not the integer.
For compilers it does not matter, for humans does.
Same as we use definitions instead of "raw" values or expressions or human readable variable names like loopCounter instead of p755_x.
Why is there a NULL in the C language?
To help make clear the assignment implies a pointer and not an integer.
Example: strtok(char *s1, const char *s2); in both cases below receive a null pointer as the NULL and 0 are both converted to a char *. The first is usually considered better self-documentation. As a style issue, follow your group's coding standard.
strtok(s, NULL);
strtok(s, 0;
Is there a context in which just plain literal 0 would not work exactly the same?
Yes - when the original type is important.
0 is an int
NULL is a void *, or int, unsigned or long or long long, etc. It is implementation defined.
Consider a function that takes a variable number of pointers, with a sentinel null pointer to indicate the last.
foo("Hello", "World", NULL); // might work if `NULL` is a pointer.
foo("Hello", "World", 0);
As the arguments as 0 and NULL are not converted when passed to a ... function (aside from some promotions), the function foo() might not access them the same. Portable code would use:
foo("Hello", "World", (char*) NULL);
// or
foo("Hello", "World", (char*) 0);
A difference may also occur when NULL, 0 are passed to _Generic
The integer constant literal 0 has different meanings depending upon the context in which it's used. In all cases, it is still an integer constant with the value 0, it is just described in different ways.
Namely, the most common purposes of NULL pointer are:
To initialize a pointer variable when that pointer variable isn’t
assigned any valid memory address yet.
To check for a null pointer before accessing any pointer
variable. By doing so, we can perform error handling in pointer
related code e.g. dereference pointer variable only if it’s not
NULL.
To pass a null pointer to a function argument when we don’t want
to pass any valid memory address.(ref)
The book Understanding and Using C Pointers, by Richard Reese says:
The null concept is an abstraction supported by the null pointer
constant. This constant may or may not be a constant zero. A C
programmer need not be concerned with their actual internal
representation.
My question is, since "this constant may or may not be a constant zero," is it safe for me to do things like the below in my code:
int *ptr = NULL;
// Some code which probably sets ptr to a valid memory address
if(!ptr)
{
ERROR();
}
If NULL is not 0, there is a chance that the if clause will evaluate to true.
Is it safe to assume that the NULL constant is zero?
NULL will compare equal to 0.
NULL is very commonly a zero bit pattern. It is possible for NULL to be a non-zero bit pattern - but not seen these days.
OP is mixing as least 4 things: NULL, null pointer constant, null pointer, comparing a null pointer to 0. C does not define a NULL constant.
NULL
NULL is a macro "which expands to an implementation-defined null
pointer constant" C17dr § 7.19 3
null pointer constant
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant. C17dr § §
6.3.2.3 3
Thus the type of a null pointer constant may be int, unsigned, long, ... or void * .
When an integer constant expression1, the null pointer constant value is 0. As a pointer like ((void *)0), its value/encoding is not specified. It ubiquitously does have the bit pattern of zeros, but is not specified so.
There may be many null pointer constants. They all compare equal to each other.
Note: the size of a null pointer constant, when it is an integer, may differ from the size of an object pointer. This size difference is often avoided by appending a L or two suffix as needed.
null pointer
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. C17dr § § 6.3.2.3 3
Conversion of a null pointer to another pointer type yields a null
pointer of that type. Any two null pointers shall compare equal. C17dr
§ § 6.3.2.3 4
The type of null pointer is some pointer, either an object pointer like int *, char * or function pointer like int (*)(int, int) or void *.
The value of a null pointer is not specified. It ubiquitously does have the bit pattern of zeros, but is not specified so.
All null pointer compare as equal, regardless of their encoding.
comparing a null pointer to 0
if(!ptr) is the same as if(!(ptr != 0)). When the pointer ptr, which is a null pointer, is compared to 0, the zero is converted to a pointer, a null pointer of the same type: int *. These 2 null pointers, which could have different bit patterns, compare as equal.
So when it is not safe to assume that the NULL constant is zero?
NULL may be a ((void*)0) and its bit pattern may differ from zeros. It does compare equal to 0 as above regardless of its encoding. Recall pointer compares have been discussed, not integer compares. Converting NULL to an integer may not result in an integer value of 0 even if ((void*)0) was all zero bits.
printf("%ju\n", (uintmax_t)(uintptr_t)NULL); // Possible not 0
Notice this is converting a pointer to an integer, not the case of if(!ptr) where a 0 was converted to a pointer.
The C spec embraces many old ways of doing things and is open to novel new ones. I have never came across an implementation where NULL was not an all zeros bit pattern. Given much code exist that assumes NULL is all zero bits, I suspect only old obscure implementations ever used a non-zero bit-pattern NULL and that NULL can be all but certain to be an all zero bit pattern.
1 The null pointer constant is 1) an integer or 2) a void*. "When an integer ..." refers to the first case, not a cast or conversion of the second case as in (int)((void*)0).
if(!ptr) is a safe way to check for a NULL pointer.
The expression !x is exactly equivalent to 0 == x. The constant 0 is a NULL pointer constant, and any pointer may be compared for equality against a NULL pointer constant.
This holds true even if the representation of a null pointer is not "all bits 0".
Section 6.5.3.3p5 of the C standard regarding the ! operator states:
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).
And section 6.3.2.3p3 regarding pointer conversions states:
An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer
constant. 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.
chux has written a good, detailed answer, but regarding that book specifically, I'd be sceptic about its quality:
This constant may or may not be a constant zero
This is wrong, it must always be a zero or a zero cast to a void*. The definition of a null pointer constant is found in C17 6.3.2.3/3:
An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. 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.
This means that all integer constant expressions like 0, 0L, 0u, 0x0, '\0' etc are null pointer constant. If any of them is cast to a void*, it is also a null pointer constant.
A C programmer need not be concerned with their actual internal representation.
The author is obviously mixing up the two formal terms null pointer constant and null pointer. A programmer do not need to concern themselves with the internal representation of a null pointer. They do need to know what makes a valid null pointer constant though. The safest, most readable way being to use the NULL macro, which is guaranteed to be a null pointer constant.
So regarding your question "is it safe for me to do things like the below in my code" - yes it is perfectly safe to do !ptr to check for a null pointer, even though ptr==NULL is more readable code.
I'm asking because of the discussion that's been provoked in this thread.
Trying to have a serious back-and-forth discussion using comments under other people's replies is not easy or fun. So I'd like to hear what our C experts think without being restricted to 500 characters at a time.
The C standard has precious few words to say about NULL and null pointer constants. There's only two relevant sections that I can find. First:
3.2.2.3 Pointers
An integral constant expression with
the value 0, or such an expression
cast to type void * , is called a null
pointer constant. If a null pointer
constant is assigned to or compared
for equality to a pointer, the
constant is converted to a pointer of
that type. Such a pointer, called a
null pointer, is guaranteed to compare
unequal to a pointer to any object or
function.
and second:
4.1.5 Common definitions
The macros are
NULL
which expands to an implementation-defined null pointer constant;
The question is, can NULL expand to an implementation-defined null pointer constant that is different from the ones enumerated in 3.2.2.3?
In particular, could it be defined as:
#define NULL __builtin_magic_null_pointer
Or even:
#define NULL ((void*)-1)
My reading of 3.2.2.3 is that it specifies that an integral constant expression of 0, and an integral constant expression of 0 cast to type void* must be among the forms of null pointer constant that the implementation recognizes, but that it isn't meant to be an exhaustive list. I believe that the implementation is free to recognize other source constructs as null pointer constants, so long as no other rules are broken.
So for example, it is provable that
#define NULL (-1)
is not a legal definition, because in
if (NULL)
do_stuff();
do_stuff() must not be called, whereas with
if (-1)
do_stuff();
do_stuff() must be called; since they are equivalent, this cannot be a legal definition of NULL.
But the standard says that integer-to-pointer conversions (and vice-versa) are implementation-defined, therefore it could define the conversion of -1 to a pointer as a conversion that produces a null pointer. In which case
if ((void*)-1)
would evaluate to false, and all would be well.
So what do other people think?
I'd ask for everybody to especially keep in mind the "as-if" rule described in 2.1.2.3 Program execution. It's huge and somewhat roundabout, so I won't paste it here, but it essentially says that an implementation merely has to produce the same observable side-effects as are required of the abstract machine described by the standard. It says that any optimizations, transformations, or whatever else the compiler wants to do to your program are perfectly legal so long as the observable side-effects of the program aren't changed by them.
So if you are looking to prove that a particular definition of NULL cannot be legal, you'll need to come up with a program that can prove it. Either one like mine that blatantly breaks other clauses in the standard, or one that can legally detect whatever magic the compiler has to do to make the strange NULL definition work.
Steve Jessop found an example of way for a program to detect that NULL isn't defined to be one of the two forms of null pointer constants in 3.2.2.3, which is to stringize the constant:
#define stringize_helper(x) #x
#define stringize(x) stringize_helper(x)
Using this macro, one could
puts(stringize(NULL));
and "detect" that NULL does not expand to one of the forms in 3.2.2.3. Is that enough to render other definitions illegal? I just don't know.
Thanks!
In the C99 standard, §7.17.3 states that NULL “expands to an implementation defined null pointer constant”. Meanwhile §6.3.2.3.3 defines null pointer constant as “an integer constant expression with the value 0, or such an expression cast to type void *”. As there is no other definition for a null pointer constant, a conforming definition of NULL must expand to an integer constant expression with the value zero (or this cast to void *).
Further quoting from the C FAQ question 5.5 (emphasis added):
Section 4.1.5 of the C Standard states that NULL “expands to an implementation-defined null pointer constant,” which means that the implementation gets to choose which form of 0 to use and whether to use a `void *` cast; see questions 5.6 and 5.7. “Implementation-defined” here does not mean that NULL might be #defined to match some implementation-specific nonzero internal null pointer value.
It makes perfect sense; since the standard requires a zero integer constant in pointer contexts to compile into a null pointer (regardless of whether or not the machine's internal representation of that has a value of zero), the case where NULL is defined as zero must be handled anyhow. The programmer is not required to type NULL to obtain null pointers; it's just a stylistic convention (and may help catch errors e.g. when a NULL defined as (void *)0 is used in a non-pointer context).
Edit: One source of confusion here seems to be the concise language used by the standard, i.e. it does not explicitly say that there is no other value that might be considered a null pointer constant. However, when the standard says “…is called a null pointer constant”, it means that exactly the given definitions are called null pointer constants. It does not need to explicitly follow every definition by stating what is non-conforming when (by definition) the standard defines what is conforming.
This expands a bit on some of the other answers and makes some points that the others missed.
Citations are to N1570 a draft of the 2011 ISO C standard. I don't believe there have been any significant changes in this area since the 1989 ANSI C standard (which is equivalent to the 1990 ISO C standard). A reference like "7.19p3" refers to subsection 7.19, paragraph 3. (The citations in the question seem to be to the 1989 ANSI standard, which described the language in section 3 and the library in section 4. All editions of the ISO standard describe the language in section 6 and the library in section 7.)
7.19p3 requires the macro NULL to expand to "an implementation-defined null pointer constant".
6.3.2.3p3 says:
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant.
Since null pointer constant is in italics, that's the definition of the term (3p1 specifies that convention) -- which implies that nothing other that what's specified there can be a null pointer constant. (The standard doesn't always strictly follow that convention for its definitions, but there's no problem assuming that it does so in this case.)
So if we want "something wacky", we need to look at what can be an "integer constant expression".
The phrase null pointer constant needs to be taken as a single term, not as a phrase whose meaning depends on its constituent words. In particular, the integer constant 0 is a null pointer constant, regardless of the context in which it appears; it needn't result in a null pointer value, and it's of type int, not of any pointer type.
An "integer constant expression with the value 0" can be any of a number of things (infinitely many if we ignore capacity limits). A literal 0 is the most obvious. Other possibilities are 0x0, 00000, 1-1, '\0', and '-'-'-'. (It's not 100% clear from the wording whether "the value 0" refers specifically to that value of type int, but I think the consensus is that 0L is also a valid null pointer constant.)
Another relevant clause is 6.6p10:
An implementation may accept other forms of constant expressions.
It's not entirely clear (to me) how much latitude this is intended to allow. For example, a compiler might support binary literals as an extension; then 0b0 would be a valid null pointer constant. It might also allow C++-style references to const objects, so that given
const int x = 0;
a reference to x could be a constant expression (it isn't in standard C).
So it's clear that 0 is a null pointer constant, and that it's a valid definition for the NULL macro.
It's equally clear that (void*)0 is a null pointer constant, but it's not a valid definition for NULL, because of 7.1.2p5:
Any definition of an object-like macro described in this clause shall
expand to code that is fully protected by parentheses where necessary,
so that it groups in an arbitrary expression as if it were a single
identifier.
If NULL expanded to (void*)0, then the expression sizeof NULL would be a syntax error.
So what about ((void*)0)? Well, I'm 99.9% sure that it's intended to be a valid definition for NULL, but 6.5.1, which describes parenthesized expressions, says:
A parenthesized expression is a primary expression. Its type and value
are identical to those of the unparenthesized expression. It is an
lvalue, a function designator, or a void expression if the
unparenthesized expression is, respectively, an lvalue, a function
designator, or a void expression.
It doesn't say that a parenthesized null pointer constant is a null pointer constant. Still, as far as I know all C compilers reasonably assume that a parenthesized null pointer constant is a null pointer constant, making ((void*)0) a valid definition for NULL.
What if a null pointer is represented not as all-bits-zero, but as some other bit pattern, for example, one equivalent to 0xFFFFFFFF. Then (void*)0xFFFFFFFF, even if it happens to evaluate to a null pointer is not a null pointer constant, simply because it doesn't satisfy the definition of that term.
So what other variations are permitted by the standard?
Since implementations may accept other forms of constant expression, a compiler could define __null as a constant expression of type int with the value 0, allowing either __null or ((void*)__null) as the definition of NULL. It could make also __null itself a constant of pointer type, but it couldn't then use __null as the definition of NULL, since it doesn't satisfy the definition in 6.3.2.3p3.
An implementation could accomplish the same thing, with no compiler magic, like this:
enum { __null };
#define NULL __null
Here __null is an integer constant expression of type int with the value 0, so it can be used anywhere a constant 0 can be used.
The advantage defining NULL in terms of a symbol like __null is that the compiler could then issue a (perhaps optional) warning if NULL is used in a non-pointer constant. For example, this:
char c = NULL; /* PLEASE DON'T DO THIS */
is perfectly legal if NULL happens to be defined as 0; expanding NULL to some recognizable token like __null would make it easier for the compiler to detect this questionable construct.
Ages later, but no one brought up this point: Suppose that the implementation does in fact choose to use
#define NULL __builtin_null
My reading of C99 is that that's fine as long as the special keyword __builtin_null behaves as-if it were either "an integral constant expression with value 0" or "an integral constant expression with value 0, cast to void *". In particular, if the implementation chooses the former of those options, then
int x = __builtin_null;
int y = __builtin_null + 1;
is a valid translation unit, setting x and y to the integer values 0 and 1 respectively. If it chooses the latter, of course, both are constraint violations (6.5.16.1, 6.5.6 respectively; void * is not a "pointer to an object type" per 6.2.5p19; 6.7.8p11 applies the constraints for assignment to initialization). And I don't offhand see why an implementation would do this if not to provide better diagnostics for "misuse" of NULL, so it seems likely that it would take the option that invalidated more code.
Well, I've found a way to prove that
#define NULL ((void*)-1)
is not a legal definition of NULL.
int main(void)
{
void (*fp)() = NULL;
}
Initializing a function pointer with NULL is legal and correct, whereas...
int main(void)
{
void (*fp)() = (void*)-1;
}
...is a constraint violation that requires a diagnostic. So that's out.
But the __builtin_magic_null_pointer definition of NULL wouldn't suffer that problem. I'd still like to know if anybody can come up with a reason why it can't be.
An integral constant expression with
the value 0, or such an expression
cast to type void * , is called a
null pointer constant.
NULL which expands to an
implementation-defined null pointer
constant;
therefore either
NULL == 0
or
NULL == (void *)0
The null pointer constant must evaluate 0, otherwise expressions like !ptr would not work as expected.
The NULL macro expands to a 0-valued expression; AFAIK, it always has.