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