This is the mail archive of the newlib@sourceware.cygnus.com mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: Definition of NULL. (fwd)



An RTEMS user recently noticed that the newlib definitions of NULL are not
proper.  The proper definition is apparently "0" or "(void *)0".  The
offending definitions are in: 

stdio.h:#define NULL    0L
stdlib.h:#define NULL 0L
string.h:#define NULL 0L
time.h:#define  NULL    0L

In newlib/libc/include.  There may be others in machine and OS specific
files.  

I have included the referenced FAQ sections with the flame bait deleted.
:)

Thanks.

--joel

----------------------------

Please see the C-FAQ.
The correct definition for NULL is
	0
or
	((void *)0)
	
I've included the salient sections of the C-FAQ  
(http://www.eskimo.com/~scs/C-faq/faq.html).

=========================================================================
Question 5.2

How do I get a null pointer in my programs?



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.)

Summary:

	Unadorned 0 okay:	Explicit cast required:

	initialization		function call,
				no prototype in scope
	assignment
				variable argument in
	comparison		varargs function call

	function call,
	prototype in scope,
	fixed argument

References: K&R1 Sec. A7.7 p. 190, Sec. A7.14 p. 192
K&R2 Sec. A7.10 p. 207, Sec. A7.17 p. 209
ANSI Sec. 3.2.2.3
ISO Sec. 6.2.2.3
H&S Sec. 4.6.3 p. 95, Sec. 6.2.7 p. 171


======================================================================
Question 5.4

What is NULL and how is it #defined?



As a matter of style, many programmers prefer not to have unadorned  
0's scattered through their programs. Therefore, the preprocessor  
macro NULL is #defined (by <stdio.h> or <stddef.h>) with the value 0,  
possibly cast to (void *) (see also question 5.6). A programmer who  
wishes to make explicit the distinction between 0 the integer and 0  
the null pointer constant can then use NULL whenever a null pointer  
is required.

Using NULL is a stylistic convention only; the preprocessor turns  
NULL back into 0 which is then recognized by the compiler, in pointer  
contexts, as before. In particular, a cast may still be necessary  
before NULL (as before 0) in a function call argument. The table  
under question 5.2 above applies for NULL as well as 0 (an unadorned  
NULL is equivalent to an unadorned 0).

NULL should only be used for pointers; see question 5.9.

References: K&R1 Sec. 5.4 pp. 97-8
K&R2 Sec. 5.4 p. 102
ANSI Sec. 4.1.5, Sec. 3.2.2.3
ISO Sec. 7.1.6, Sec. 6.2.2.3
Rationale Sec. 4.1.5
H&S Sec. 5.3.2 p. 122, Sec. 11.1 p. 292

======================================================================Question  
5.6

If NULL were defined as follows:

	#define NULL ((char *)0)

wouldn't that make function calls which pass an uncast NULL work?



Not in general. The problem is that there are machines which use  
different internal representations for pointers to different types of  
data. The suggested definition would make uncast NULL arguments to  
functions expecting pointers to characters work correctly, but  
pointer arguments of other types would still be problematical, and  
legal constructions such as

	FILE *fp = NULL;

could fail.

Nevertheless, ANSI C allows the alternate definition

	#define NULL ((void *)0)

for NULL. Besides potentially helping incorrect programs to work  
(but only on machines with homogeneous pointers, thus questionably  
valid assistance), this definition may catch programs which use NULL  
incorrectly (e.g. when the ASCII NUL character was really intended;  
see question 5.9).

References: Rationale Sec. 4.1.5

==========================================================================	 

---
Eric Norum                                 eric@skatter.usask.ca
Saskatchewan Accelerator Laboratory        Phone: (306) 966-6308
University of Saskatchewan                 FAX:   (306) 966-6058
Saskatoon, Canada.