6

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?

duozmo
  • 1,698
  • 5
  • 22
  • 33
  • Just my comment on this: GCC apparently and evidently is *not good*, I'd use a different word if it wasn't inappropriate. – Utkan Gezer Apr 24 '14 at 15:37
  • What does `gcc -E nulltest.c | tail -n 6` produce? – pmg Apr 24 '14 at 15:48
  • “NULL should expand to a null pointer constant”: quite right. However, if you look at the definition of “null pointer constant” in the standard, you will find that a null pointer constant is not guaranteed to have a pointer type. – Pascal Cuoq Apr 24 '14 at 16:11
  • gcc (Debian 4.7.2-5) 4.7.2 does issue the warning. – alk Apr 24 '14 at 16:27

1 Answers1

9

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • That helps considerably. This followup is a bit absurd but: Formally speaking, isn't it wrong for an implementation to expand `NULL` to `0` (representing a null pointer constant, not an int) and then interpret that `0` as an int with value zero instead of a null pointer constant? (The C standard [defines](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3) an "integer constant expression with the value 0" as a null pointer constant (in [pointer contexts](http://c-faq.com/null/nullor0.html)), not the reverse.) Put another way, `char c = NULL` should be inequivalent to `char c = 0`, right? – duozmo Apr 25 '14 at 15:44
  • @duozmo: See my updated answer; I've just added the last 3 paragraphs. – Keith Thompson Apr 25 '14 at 15:54
  • Okay, thanks again Keith. I also recommend to folks who want this from a different angle the question and your answer at: [Can a conforming C implementation #define NULL to be something wacky?](http://stackoverflow.com/questions/2599207/can-a-conforming-c-implementation-define-null-to-be-something-wacky) – duozmo Apr 26 '14 at 15:53