Take this brief C file, nulltest.c, which prints "Hey":
#include <stddef.h>
#include <stdio.h>
int main() {
char c = NULL;
c = 'e';
printf("H%cy\n", c);
return 0;
}
My understanding is that in C, NULL
should expand to a null pointer constant, which would make char c = NULL
an implicit cast of pointer to integer. However, when compiling this in gcc 4.8:
$ gcc --version
gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1
$ gcc -Wall -Wconversion nulltest.c
$
I get no warnings.
On the other hand, both a prior version of gcc and clang warn on the same code:
$ gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)
$ gcc nulltest.c
nulltest.c: In function ‘main’:
nulltest.c:5: warning: initialization makes integer from pointer without a cast
On Mac OS X 10.9:
$ clang --version
Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn)
$ clang nulltest.c
nulltest.c:5:8: warning: incompatible pointer to integer conversion initializing
'char' with an expression of type 'void *' [-Wint-conversion]
char c = NULL;
^ ~~~~
For both gcc 4.4 and 4.8, I believe the relevant line in the relevant copy of stddef.h reads #define NULL ((void *)0)
.
Why does one version of gcc warn and not the other?
Should they warn? Sure. Are they required to? No.
A null pointer constant is not necessarily of pointer type; in fact it typically isn't. (Yes, this is as weird as you think it is.)
A null pointer constant is either an constant integer expression with value 0, or such an expression cast to void*
. So an implementation may have:
#define NULL 0
Which, unfortunately, means that it won't report an error on
char c = NULL;
An implementation could also have:
#define NULL ((void*)0)
which would avoid that particular problem -- but not all implementations do so. (A side note: In C++, ((void*)0)
is not a valid null pointer constant.)
There are other tricks compilers can play to detect such logical errors. For example:
enum { __NULL__ };
#define NULL __NULL__
or even:
#define NULL __magic_builtin_null_pointer_constant__
NULL
still expands to an expression of type int
with value zero, but a compiler could potentially detect the use of this particular expression (if it retains that information internally) and issue a warning.
But ultimately it's up to you as a programmer to avoid this particular error. Unfortunately, you can't depend on the compiler to detect it for you.
Another implication of this is that if need to pass a null pointer as an argument to a function with a variable number of parameters, you can't safely pass just NULL
; you have to cast it to the appropriate pointer type. The execl*()
functions are the most common example of this.
The standard defines the term null pointer constant, but it doesn't say that a null pointer constant is an expression with pointer type. The expression 0
is always of type int
-- and it's always a null pointer constant, even when used in a non-pointer context like
int n = 0;
(And it's an octal constant.) The term null pointer constant needs to be understood as a single concept defined by the standard, not as a phrase whose meaning is derived from its constituent words.
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