According to the language definition, a constant 0 in a pointer
context is converted into a null pointer at compile time. That is, in
an initialization, assignment, or comparison when one side is a
variable or expression of pointer type, the compiler can tell that a
constant 0 on the other side requests a null pointer, and generate
the correctly-typed null pointer value. Therefore, the following
fragments are perfectly legal:
char *p = 0;
if(p != 0)
(See also question 5.3.)
However, an argument being passed to a function is not necessarily
recognizable as a pointer context, and the compiler may not be able
to tell that an unadorned 0 ``means'' a null pointer. To generate a
null pointer in a function call context, an explicit cast may be
required, to force the 0 to be recognized as a pointer. For example,
the Unix system call execl takes a variable-length,
null-pointer-terminated list of character pointer arguments, and is
correctly called like this:
execl("/bin/sh", "sh", "-c", "date", (char *)0);
If the (char *) cast on the last argument were omitted, the compiler
would not know to pass a null pointer, and would pass an integer 0
instead. (Note that many Unix manuals get this example wrong .)
When function prototypes are in scope, argument passing becomes an
``assignment context,'' and most casts may safely be omitted, since
the prototype tells the compiler that a pointer is required, and of
which type, enabling it to correctly convert an unadorned 0. Function
prototypes cannot provide the types for variable arguments in
variable-length argument lists however, so explicit casts are still
required for those arguments. (See also question 15.3.) It is safest
to properly cast all null pointer constants in function calls: to
guard against varargs functions or those without prototypes, to allow
interim use of non-ANSI compilers, and to demonstrate that you know
what you are doing. (Incidentally, it's also a simpler rule to
remember.)