Actually, you can use a literal 0 anyplace you would use NULL.

Section 6.3.2.3p3 of the C standard states:

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

And section 7.19p3 states:

The macros are:

NULL

which expands to an implementation-defined null pointer constant

So 0 qualifies as a null pointer constant, as does (void *)0 and NULL. The use of NULL is preferred however as it makes it more evident to the reader that a null pointer is being used and not the integer value 0.

Answer from dbush on Stack Overflow
Top answer
1 of 2
7

Actually, you can use a literal 0 anyplace you would use NULL.

Section 6.3.2.3p3 of the C standard states:

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

And section 7.19p3 states:

The macros are:

NULL

which expands to an implementation-defined null pointer constant

So 0 qualifies as a null pointer constant, as does (void *)0 and NULL. The use of NULL is preferred however as it makes it more evident to the reader that a null pointer is being used and not the integer value 0.

2 of 2
5

NULL is used to make it clear it is a pointer type.

Ideally, the C implementation would define NULL as ((void *) 0) or something equivalent, and programmers would always use NULL when they want a null pointer constant.

If this is done, then, when a programmer has, for example, an int *x and accidentally writes *x = NULL;, then the compiler can recognize that a mistake has been made, because the left side of = has type int, and the right side has type void *, and this is not a proper combination for assignment.

In contrast, if the programmer accidentally writes *x = 0; instead of x = 0;, then the compiler cannot recognize this mistake, because the left side has type int, and the right side has type int, and that is a valid combination.

Thus, when NULL is defined well and is used, mistakes are detected earlier.

In particular answer to your question “Is there a context in which just plain literal 0 would not work exactly the same?”:

  • In correct code, NULL and 0 may be used interchangeably as null pointer constants.
  • 0 will function as an integer (non-pointer) constant, but NULL might not, depending on how the C implementation defines it.
  • For the purpose of detecting errors, NULL and 0 do not work exactly the same; using NULL with a good definition serves to help detect some mistakes that using 0 does not.

The C standard allows 0 to be used for null pointer constants for historic reasons. However, this is not beneficial except for allowing previously written code to compile in compilers using current C standards. New code should avoid using 0 as a null pointer constant.

Top answer
1 of 11
445

Note: This answer applies to the C language, not C++.


Null Pointers

The integer constant literal 0 has different meanings depending upon the context in which it's used. In all cases, it is still an integer constant with the value 0, it is just described in different ways.

If a pointer is being compared to the constant literal 0, then this is a check to see if the pointer is a null pointer. This 0 is then referred to as a null pointer constant. The C standard defines that 0 cast to the type void * is both a null pointer and a null pointer constant.

Additionally, to help readability, the macro NULL is provided in the header file stddef.h. Depending upon your compiler it might be possible to #undef NULL and redefine it to something wacky.

Therefore, here are some valid ways to check for a null pointer:

if (pointer == NULL)

NULL is defined to compare equal to a null pointer. It is implementation defined what the actual definition of NULL is, as long as it is a valid null pointer constant.

if (pointer == 0)

0 is another representation of the null pointer constant.

if (!pointer)

This if statement implicitly checks "is not 0", so we reverse that to mean "is 0".

The following are INVALID ways to check for a null pointer:

int mynull = 0;
<some code>
if (pointer == mynull)

To the compiler this is not a check for a null pointer, but an equality check on two variables. This might work if mynull never changes in the code and the compiler optimizations constant fold the 0 into the if statement, but this is not guaranteed and the compiler has to produce at least one diagnostic message (warning or error) according to the C Standard.

Note that the value of a null pointer in the C language does not matter on the underlying architecture. If the underlying architecture has a null pointer value defined as address 0xDEADBEEF, then it is up to the compiler to sort this mess out.

As such, even on this funny architecture, the following ways are still valid ways to check for a null pointer:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)

The following are INVALID ways to check for a null pointer:

#define MYNULL (void *) 0xDEADBEEF
if (pointer == MYNULL)
if (pointer == 0xDEADBEEF)

as these are seen by a compiler as normal comparisons.

Null Characters

'\0' is defined to be a null character - that is a character with all bits set to zero. '\0' is (like all character literals) an integer constant, in this case with the value zero. So '\0' is completely equivalent to an unadorned 0 integer constant - the only difference is in the intent that it conveys to a human reader ("I'm using this as a null character.").

'\0' has nothing to do with pointers. However, you may see something similar to this code:

if (!*char_pointer)

checks if the char pointer is pointing at a null character.

if (*char_pointer)

checks if the char pointer is pointing at a non-null character.

Don't get these confused with null pointers. Just because the bit representation is the same, and this allows for some convenient cross over cases, they are not really the same thing.

References

See Question 5.3 of the comp.lang.c FAQ for more. See this pdf for the C standard. Check out sections 6.3.2.3 Pointers, paragraph 3.

2 of 11
45

It appears that a number of people misunderstand what the differences between NULL, '\0' and 0 are. So, to explain, and in attempt to avoid repeating things said earlier:

A constant expression of type int with the value 0, or an expression of this type, cast to type void * is a null pointer constant, which if converted to a pointer becomes a null pointer. It is guaranteed by the standard to compare unequal to any pointer to any object or function.

NULL is a macro, defined in as a null pointer constant.

\0 is a construction used to represent the null character, used to terminate a string.

A null character is a byte which has all its bits set to 0.

Top answer
1 of 3
12

In C, NULL is a macro that expands either to 0 or (void*)0 (or something that has a similar effect).

In the first case, you can not differentiate between NULL and 0, because they are literally the same.
In the second case, your code will cause a compile error, because you can't compare an integer variable with a pointer.

2 of 3
4

First some background ...


The macros are NULL which expands to an implementation-defined null pointer constant; C11 §7.19 3

NULL typically is an integer constant 0 or (void*)0 or the like. It may have a different implementation or type - It could be ((int*) 0xDEADBEEF) as strange as that may be.

NULL might be type int. It might be type void * or something else. The type of NULL is not defined.


When the null pointer constant NULL is cast to any pointer, is is a null pointer. An integer 0 cast to a pointer is also a null pointer. A system could have many different (bit-wise) null pointers. They all compare equally to each other. They all compare unequally to any valid object/function. Recall this compare is done as pointers, not integers.

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function. C11 §6.3.2.3 3

int x;
if (&x == NULL) ... // this is false

So after all that chapter and verse how to distinguish NULL from 0?

If the macro NULL is defined as an int 0 - it is game over - there is no difference between 0 and NULL.

If NULL is not an int, then code can use _Generic() to differentiate NULL and 0. This does not help OP's "Any change made can only be made within the function itself." requirement as that function accepts an int augment.

If NULL is an int that has a different bit-pattern than 0, then a simple memcmp() can differentiate.

I suspect the whole reason for this exercise is to realize there is no portable method to distinguish NULL from 0.

Top answer
1 of 5
21

The macro NULL is a null-pointer constant and has either an integer type or a pointer type.

Using NULL to assign or initialize a non-pointer variable will lead to question marks from other programmers at the least and it might result in compiler failures.
A line like

int a = NULL;

is not considered good code and it will make the code less readable.

2 of 5
9

I think @R Sahu's answer reaches the right conclusion, but the supporting evidence it provides (based on a single implementation) is somewhat weak, at best.

This is tagged with both c and c++. The details of how NULL is defined vary between the two, and also varies over time for C++. In all cases, NULL must expand to an "implementation defined null pointer constant". What varies is the definition of "null pointer constant".

In C, a null pointer constant must be an integer literal with the value 0, or the same cast to type "pointer to void"1. An integer with a non-zero value (by itself, or cast to type "pointer to void") is not allowed2. So, 0, 0L and ((void *)0) are all allowed, but something like ((void *)1234) is not.

In C++ 98/03, NULL must also expand to a null pointer constant--but with a somewhat different definition of the term--in particular, casting the integer literal to type "pointer to void" is not allowed in C++ 98/03. This means 0 and 0L are both allowed (and so would '\0' or, if you wanted to be really perverse, (3-(2 + 1)).

In C++ 11, the nullptr_t type and nullptr literal were added to C++, and nullptr is also allowed as a null pointer constant3. NULL is allowed to expand to any null pointer constant, so it could be 0, 0L (etc.) or nullptr, but (again) cannot be any non-zero integer, nor can it have type "pointer to void" (or "pointer to char", etc.)

The intent, however, has always been that NULL only be used to represent a null pointer. Although both C and C++ allow it to be defined as an unadorned 0 (for one example), so it might be possible to assign it to a variable of type int, there's no guarantee that code that does so will even compile (and a fair number of people believe that it shouldn't compile).

Conclusion: you should only ever assign NULL to a pointer. For new code, I'd advise using NULL only in C, and using nullptr in C++. For existing C++ code, I'd convert to using nullptr when any other refactoring is being done on that code, but would not usually modify the code solely to change NULL to nullptr (but code that assigns NULL to any non-pointer type should be corrected immediately, in either C or C++).


1. The wording in the C standard (§6.3.2.3/3) reads:

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

The italics (which are in the original) mean that this is considered the definition of that term for this standard.

2. Assigning a null pointer constant to a pointer variable may result in a non-zero value being stored into that variable. Although somewhat unusual, this is perfectly allowable. Even if/when that is the case, however, comparing that variable to a null pointer constant (e.g., NULL or 0) must yield true.

Likewise, an implementation is free to define any number of other integer constants that will produce a null pointer when assigned to a pointer variable. This doesn't affect the requirement on how NULL must be defined.

3. Here, the official wording (from [conv.ptr]) reads:

A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t.

Here again, the italics indicate that this is the official definition of the term for that standard.

Top answer
1 of 6
30

In C and C++, pointers are inherently unsafe, that is, when you dereference a pointer, it is your own responsibility to make sure it points somewhere valid; this is part of what "manual memory management" is about (as opposed to the automatic memory management schemes implemented in languages like Java, PHP, or the .NET runtime, which won't allow you to create invalid references without considerable effort).

A common solution that catches many errors is to set all pointers that don't point to anything as NULL (or, in correct C++, 0), and checking for that before accessing the pointer. Specifically, it is common practice to initialize all pointers to NULL (unless you already have something to point them at when you declare them), and set them to NULL when you delete or free() them (unless they go out of scope immediately after that). Example (in C, but also valid C++):

void fill_foo(int* foo) {
    *foo = 23; // this will crash and burn if foo is NULL
}

A better version:

void fill_foo(int* foo) {
    if (!foo) { // this is the NULL check
        printf("This is wrong\n");
        return;
    }
    *foo = 23;
}

Without the null check, passing a NULL pointer into this function will cause a segfault, and there is nothing you can do - the OS will simply kill your process and maybe core-dump or pop up a crash report dialog. With the null check in place, you can perform proper error handling and recover gracefully - correct the problem yourself, abort the current operation, write a log entry, notify the user, whatever is appropriate.

2 of 6
8

The other answers pretty much covered your exact question. A null check is made to be sure that the pointer you received actually points to a valid instance of a type (objects, primitives, etc).

I'm going to add my own piece of advice here, though. Avoid null checks. :) Null checks (and other forms of Defensive Programming) clutter code up, and actually make it more error prone than other error-handling techniques.

My favorite technique when it comes to object pointers is to use the Null Object pattern. That means returning a (pointer - or even better, reference to an) empty array or list instead of null, or returning an empty string ("") instead of null, or even the string "0" (or something equivalent to "nothing" in the context) where you expect it to be parsed to an integer.

As a bonus, here's a little something you might not have known about the null pointer, which was (first formally) implemented by C.A.R. Hoare for the Algol W language in 1965.

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Find elsewhere
Top answer
1 of 7
26

The definition of NULL is a syntactic crutch to allow a programmer a clear expression that a pointer is, at certain times, pointing nowhere. It's meant for readability - and with increased compiler bloat even for automated checks.

On a hardware level there is no such thing. A pointer is a word, and a word always holds a valid number. So by convention zero was chosen - it could have been any other. Like -1 for example. Selecting 0 offered the advantage that a simple if(pointer) could be used to check if it's a valid pointer (or more correct, not 0).

So my question is what led some C standard to treat the NULL pointer differently from any other pointer?

C also doesn't treat NULL different from any other pointer. The C library in contrast does, but not because of NULL, but rather as its (runtime) value of 0 will make some functions fail, when used as input.

Did K&R want to target an exotic architecture or something?

No. It's a concept needed from a pure software engineering point of view. Having a syntactic construct to handle uninitialized pointers improves readability and possibly enables further checks.


Now, for the historic part, NULL is, like the related handling of TRUE/FALSE inherited from BCPL (through B). Just here it was called nil.


Even with that little historic bit, I'm not sure if Retrocomputing is the right place to ask for this, as it's about a basic concept in software engineering and not anything burdened with historic implication. So a quick search in Software Engineering and Stack Overflow shows that this question has been asked many times. It's something people always stumble on, isn't it?

2 of 7
24

The important thing you may be missing is that a null pointer in C is not required by the standard to have the same binary representation as the number zero. It is still a "normal" pointer, but it points to a special location that the program is not allowed to use.

The integer constant 0 is turned into nullptr when used as a pointer. Similarly, 0 will become 0.0 when used in floating-point calculations, or false in boolean operations. Coercions are not just one-way: if(ptr) will convert a pointer into a boolean which indicates whether the pointer is not null. All of these conversions serve to avoid requiring that a null pointer shares the same representation as zero.

Most machines represent integer zero as all-bits-zero and comparisons against that are particularly cheap, so it is worthwhile to arrange things so that sentinel values such as null pointers and end-of-string markers are all-bits-zero, and also that +0.0 and false are also all-bits-zero.

There is some subtlety in C in that a literal 0 is converted into nullptr, whereas casting an integer that happens to be zero into a pointer will produce a pointer to location zero. Because they are the same thing on modern platforms, a whole class of potential bugs go away.

Top answer
1 of 3
2

The code for pushing (and popping) the stack is wrong. Also, I was confused for a while reading your code because top actually represents array of stack elements, not just the top item.

In particular, malloc expects a number of bytes, however you have passed it a number of elements. And it is poor style to use void * when you mean void **.

I'd suggest making your code more readable by using [] notation and renaming top:

void **new_content = malloc( (stack->size + 1) * sizeof *new_content);
if ( !new_content)
     // error handling...

if ( stack->size > 0 )
    memcpy( &new_content[1], &stack->content[0], stack->size * sizeof *new_content );

new_content[0] = item;
free(stack->content);
stack->content = new_content;
++stack_size;

Now, this is actually about the worst possible way to implement a stack, in terms of efficiency. Every single push and pop operation you have to do an allocation and copy the entire stack. If you actually make your stack have its "top" at the end, then things become a whole lot simpler:

 void **new_content = realloc( stack->content, (stack->size + 1) * sizeof *new_content );
 if ( !new_content )
      // error handling...

 new_content[stack->size++] = item;
 stack->content = new_content;

Similar comments apply to your pop function in both varieties.

2 of 3
1

In stack_push():

void *new_top = malloc(stack->size);
memcpy(new_top + sizeof(void*), stack->top, (stack->size - 1) * sizeof(void *));

This has several problems:

  • You are allocating the wrong size. You want to allocate stack->size number of items times the size of whatever stack->top points to.
  • You must not call memcpy() when stack->top is 0. The behaviour is undefined if you copy to or from a null pointer.
  • You try to perform pointer arithmetic on a void *. This is a compiler extension (eg. gcc) and is not possible in standard C.

The easiest way to solve these problems is to declare new_top to be the same type as stack->top:

void **old_top = stack->top;
void **new_top = malloc(stack->size * sizeof *new_top);
if (stack->top) {
  memcpy (new_top + 1, stack->top, (stack->size - 1) * sizeof *new_top);
}
stack->top = new_top;

Similarly, in stack_pop():

void **old_top = stack->top;
void *item = *stack->top;

void **new_top = malloc(stack->size * sizeof *new_top);
if (stack->size) {
  memcpy(new_top, stack->top + 1, stack->size * sizeof *new_top);
}
stack->top = new_top;
🌐
Flavio Copes
flaviocopes.com › home › how to use null in c
How to use NULL in C - Flavio Copes
February 13, 2020 - 87 courses, 17 books, 222 tools. ... SponsoredCreemSell Software Globally - with zero headaches+Become a sponsorGet your product in front of developers. ... A brief guide to NULL in C, where it stands only for a null pointer, why you include stdio.h or stddef.h to use it, and how to check a pointer against it.
🌐
Medium
medium.com › @shlomohassid › null-how-do-you-define-nothing-and-why-would-you-07683bdbe63a
NULL: How Do You Define Nothing? And Why Would You? | by Momi | Medium
May 31, 2025 - We’ve set p to null and q to point to x. In memory, it might look like this: Stack: [0x7FFEEFD0] p = 0x00000000 (NULL, points to nothing) [0x7FFEEFD8] q = 0x601060 (address of x) Heap: (not used in this example) ...
🌐
W3Schools
w3schools.com › c › c_null.php
C NULL
NULL is a special value that represents a "null pointer" - a pointer that does not point to anything.
🌐
SANS Institute
sans.org › blog › stack canaries – gingerly sidestepping the cage
Stack Canaries – Gingerly Sidestepping the Cage | SANS Institute
July 13, 2026 - Table 3. Types of canaries. Many buffer overflow vulnerabilities are caused by string operations such as gets(), strcpy(), read(). Strings in C are commonly terminated using a single NULL byte (0x00).
🌐
Medium
medium.com › onrock › explicit-non-null-pointers-in-c-fa4cccb40198
Explicit non-null pointers in C. Browsing this thread on Stack Overflow… | by Kyle Van Berendonck | OnRock Engineering | Medium
December 15, 2017 - Unfortunately, it’s not all that smart. We can circumvent both gcc and clang quite easily by just assigning a local to NULL and passing it:
🌐
GeeksforGeeks
geeksforgeeks.org › c language › null-pointer-in-c
NULL Pointer in C - GeeksforGeeks
We can pass a NULL value for pointers that should not contain any value to the function in C.
Published: January 10, 2025