Jens Gustedt's Blog

November 7, 2010

Don’t use NULL

Filed under: C99, integers, syntax — Jens Gustedt @ 23:36

I always thought that using NULL whenever I wanted to assign a null pointer value to a pointer was a good thing, but today I learned the contrary.

The standard,, says, seemingly innocent

An integer constant expression with the value 0, or such an expression cast to type void*, is called a null pointer constant.

An integer constant expression is something that evaluates at compile time and is of an integer type. Integer types are all the standard integer types that we all know, including plain char and _Bool, and enumeration types. In particular such expressions may contain casts to such types.

So far this all sounds relatively innocent if you think of the primary use of null pointer constants namely to be assigned to pointer variables, but see below.

Then later (7.17) is defined the macro

NULL which expands to an implementation-defined null pointer constant

This means that NULL may result in at least 12 different standard integer types and many more expressions that result in a null value in that type:

(unsigned char)0
(signed char)0
(unsigned short)0
(signed short)0
(unsigned)0 == 0U
(signed)0 == 0 == '\0' == enumeration_constant_of_value_0
(unsigned long)0 == 0UL
(signed long)0 == 0L
(unsigned long long)0 == 0ULL
(signed long)0 == 0LL

and in


and for any enumeration type enum etag or any typedef tdef of integer type in

(enum etag)0, (tdef)0.

What is important here that most of these expressions are only interpreted as null pointer constants when they appear in what some people call a pointer context that is in a context where the compiler expects a pointer value. But strictly speaking C, doesn’t know such a thing like a pointer or integer context.

This becomes harmful when you pass arguments to va_arg functions. Here most of the arguments are just placed on the stack as they are, only small integer types are promoted to int. Now consider a harmless function such as printf that at some point expects a void* pointer:

printf("%d (%p) %d", 1, NULL, 2);

Looks fine? But it isn’t at all. As we have seen depending on the mood of your compiler provider NULL here may be many things: something what most people expect, namely (void*)0, but also 0L or 0LL. The call to printf might simply expect the three arguments on wrong places on the stack and crash your program.

Now people seriously propose the following work around for that

printf("%d (%p) %d", 1, (void*)NULL, 2);

But wasn’t the only justification of the macro NULL just that it should remind us that there is a pointer type expected at a certain place? But this fact is already clear from the cast to void*. Why not just use

printf("%d (%p) %d", 1, (void*)0, 2);

that has the same message?

In summary,

  • NULL obfuscates an expression that can be either of integer type or of pointer type.
  • Whenever you want to initialize or assign to a pointer to a null value, use plain 0, it does serve well.
  • Whenever you use a null value in a function call for a parameter that has no prototype use (T*)0.

Ah, and yes, an enumeration casted 0‘s should be interpreted as null pointer constants:

enum awk { a };
void* q = (enum awk)0;

clang accepts this correctly. gcc and opencc don’t.

About these ads


  1. 0 is not that good either:

    Comment by Thomas — November 26, 2010 @ 08:23

    • No I don’t agree with the blog that you link to. Already most of it applies to C++, and not to C. But I think that the idea of nullptr as such is not such a bad idea. I agree that the cavalresque manner of imposing again a new conflicting keyword is really annoying. If they would be nice guys that cared about the rest of the world they would use _Nullptr as a keyword and put a macro nullptr in something like “stdnull.h”.

      But for 0 itself, I don’t agree. According to the C99 standard 0 is always an integer constant. (Yes it is an octal constant, but who cares :) It may implicitly cast to a “null pointer constant” only because it is a constant integer expression of value 0. But it never is a “null pointer” by itself.

      Now the example that is given there: printf("%p\n", 0). Integer promotion rules define what has to happen: it pushes an int on the stack. printf expects a void*. This is clearly undefined behavior as soon as both types differ in width, which they do on many platforms nowadays.

      Comment by Jens Gustedt — November 26, 2010 @ 09:09

  2. (enum awk)0 is not a “integer constant expression with the value 0, or such an expression cast to type void*”, so if clang accepts it without a diagnostic, then thats a compiler extension. gcc behaviour is correct.

    see 6.4.4 for the deifnition of integer constant.

    Comment by anonymous — June 10, 2012 @ 14:31

    • Usually I wouldn’t allow for anonymous comments, but this one is important so I’ll correct it.

      (enum awk)0 is not an “integer constant”, yes, but confusingly it is a “integer constant expression” as defined by the standard. The relevant explanation for that is in 6.6p6:

      An integer constant expression shall have integer type and shall only have operands
      that are integer constants, enumeration constants, character constants, sizeof
      expressions whose results are integer constants, and floating constants that are the
      immediate operands of casts.

      Integer types include enumerations, 0 is an integer constant and the expression is an immediate operand of a cast.

      Comment by Jens Gustedt — June 10, 2012 @ 14:50

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Silver is the New Black Theme Blog at


Get every new post delivered to your Inbox.

%d bloggers like this: