Skip to content

Understanding NULL Pointers in C

This article is intended for intermediate and advanced C programmers. It assumes familiarity with pointers and basic memory management concepts and explains the purpose of NULL pointers, their common uses, and how they help write safer and more reliable C programs.


1. What Is a NULL Pointer?

A NULL pointer is a pointer that does not point to any valid memory location. It is used to indicate that a pointer is intentionally not associated with an object or function.

In C, the macro NULL is defined in standard header files such as <stddef.h> and <stdlib.h>. Assigning NULL to a pointer indicates that it currently points to nothing.

For example:

#include <stdio.h>

int main(void)
{
    int *ptr = NULL;

    return 0;
}

In this example, ptr is a valid pointer variable, but it does not point to any integer. The pointer has been explicitly initialized to NULL, indicating that it is not currently referencing an object.

It is important to distinguish a NULL pointer from an uninitialized pointer. An uninitialized pointer contains an indeterminate value and may point to an arbitrary memory location, whereas a NULL pointer has a well-defined value that represents "no valid address."

int *ptr1;        /* Uninitialized pointer */
int *ptr2 = NULL; /* NULL pointer */

Initializing pointers to NULL is considered good programming practice because it clearly indicates that the pointer is not yet being used and helps prevent accidental access to invalid memory.


2. Initializing Pointers to NULL

Initializing a pointer to NULL is a good programming practice because it ensures that the pointer has a well-defined value before it is used. An uninitialized pointer contains an indeterminate value, which may lead to undefined behavior if it is accidentally dereferenced.

For example:

int *ptr = NULL;

At this point, ptr does not point to any valid object. It simply indicates that no memory or object has been assigned to the pointer.

Later in the program, the pointer can be assigned the address of an object or dynamically allocated memory.

int value = 100;

ptr = &value;

or

ptr = malloc(sizeof(int));

Once the pointer has been assigned a valid address, it can be safely dereferenced.

Initializing pointers to NULL also makes it easy to determine whether a pointer has been assigned a valid address.

if (ptr == NULL)
{
    printf("Pointer has not been initialized.\n");
}
else
{
    printf("%d\n", *ptr);
}

Using NULL in this way makes programs easier to understand and debug, especially when working with multiple pointers or dynamically allocated memory.


3. Dereferencing a NULL Pointer

Although a NULL pointer is a valid value for a pointer, it must never be dereferenced. Since a NULL pointer does not refer to a valid memory location, attempting to read from or write to it results in undefined behavior.

For example:

#include <stdio.h>

int main(void)
{
    int *ptr = NULL;

    printf("%d\n", *ptr);

    return 0;
}

In this example, ptr does not point to a valid integer. Attempting to dereference it using *ptr may cause the program to crash or exhibit other unpredictable behavior.

To avoid this problem, a pointer should be checked before it is dereferenced whenever there is a possibility that it may be NULL.

if (ptr != NULL)
{
    printf("%d\n", *ptr);
}
else
{
    printf("Pointer is NULL.\n");
}

Checking for NULL before dereferencing is a common defensive programming practice. It helps prevent invalid memory access and makes programs more robust and easier to debug.


4. Setting Pointers to NULL After free()

When dynamically allocated memory is released using free(), the memory becomes available for reuse. However, the pointer itself is not modified. It continues to hold the address of the memory that has just been released.

For example:

int *ptr = malloc(sizeof(int));

if (ptr == NULL)
{
    return 1;
}

free(ptr);

After the call to free(), ptr still contains the same memory address, but that address is no longer valid. At this point, ptr becomes a dangling pointer.

To prevent accidentally using a dangling pointer, it is good practice to assign NULL to the pointer immediately after freeing the memory.

free(ptr);
ptr = NULL;

Setting the pointer to NULL has two important benefits:

  • It clearly indicates that the pointer no longer refers to valid memory.
  • It allows the program to safely test whether the pointer has been invalidated before attempting to use it.
if (ptr != NULL)
{
    /* Safe to use ptr */
}

Assigning NULL after calling free() is a simple defensive programming technique that helps prevent bugs caused by accidentally dereferencing dangling pointers.


5. Common Uses of NULL Pointers

NULL pointers are widely used in C programs to indicate that a pointer is not currently referencing an object. They provide a consistent way to represent the absence of valid data.

Some common uses include:

Indicating Memory Allocation Failure

The malloc(), calloc(), and realloc() functions return NULL if they are unable to allocate the requested memory.

int *ptr = malloc(sizeof(int));

if (ptr == NULL)
{
    printf("Memory allocation failed.\n");
}

Representing an Empty Linked List

A linked list that contains no nodes is typically represented by a head pointer initialized to NULL.

Node *head = NULL;

Indicating Missing Child Nodes

In tree data structures, a node with no left or right child typically stores NULL in the corresponding pointer.

node->left = NULL;
node->right = NULL;

Returning "No Result"

Functions that return pointers often return NULL to indicate that no valid object could be returned.

char *find_name(const char *name)
{
    /* Search fails */

    return NULL;
}

The calling function can then determine whether the operation was successful by checking the returned pointer.

char *result = find_name("Alice");

if (result == NULL)
{
    printf("Name not found.\n");
}

Using NULL consistently makes programs easier to understand because it provides a well-defined way to represent the absence of a valid object or memory location.


6. Dangling Pointer vs. NULL Pointer

A NULL pointer and a dangling pointer are fundamentally different concepts, although they are often confused.

A NULL pointer intentionally points to no valid memory location. It is safe to compare against NULL and assign to a pointer, but it must not be dereferenced.

int *ptr = NULL;

A dangling pointer, on the other hand, points to a memory location that was once valid but is no longer valid. This commonly occurs after dynamically allocated memory has been released using free().

int *ptr = malloc(sizeof(int));

free(ptr);      /* ptr is now a dangling pointer */

A dangling pointer can be converted into a NULL pointer by explicitly assigning NULL after the memory has been freed.

free(ptr);
ptr = NULL;

The following table summarizes the differences.

NULL Pointer Dangling Pointer
Points to no valid memory location. Points to memory that is no longer valid.
Intentionally initialized to NULL. Typically created after free() or by returning the address of a local variable.
Safe to compare with NULL. Appears valid but references invalid memory.
Must not be dereferenced. Must not be dereferenced.
Can be safely passed to free(). Passing a dangling pointer to free() again results in undefined behavior.

Although neither type of pointer should ever be dereferenced, a NULL pointer is generally safer because it explicitly indicates that no valid object is being referenced. A dangling pointer, however, still contains an address, making it much easier to accidentally access invalid memory.


7. Summary

A NULL pointer is a pointer that intentionally does not reference a valid memory location. It provides a well-defined way to indicate that a pointer is not currently associated with an object or memory block.

This article explained the purpose of NULL pointers and demonstrated how they can be used to:

  • Initialize pointers to a known value.
  • Prevent the use of uninitialized pointers.
  • Check whether a pointer is valid before dereferencing it.
  • Invalidate pointers after calling free().
  • Represent the absence of an object or indicate failure in functions that return pointers.

It is important to distinguish a NULL pointer from other pointer-related concepts. An uninitialized pointer contains an indeterminate value, while a dangling pointer refers to memory that is no longer valid. A NULL pointer, however, is intentionally assigned a well-defined value that represents "points to nothing."

Using NULL pointers consistently is considered good programming practice because it makes programs safer, easier to understand, and less prone to pointer-related errors.


C/C++ Material on this website

(such as memory managment, pointers, embedded C programming etc.)