Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should C compilers warn on 'char c = NULL'?

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?

like image 636
duozmo Avatar asked Apr 24 '14 15:04

duozmo


1 Answers

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.

like image 145
Keith Thompson Avatar answered Oct 21 '22 04:10

Keith Thompson