C nullptr (C23)

Type-safe null pointer constant in modern C

🎯 What is nullptr?

The nullptr keyword in C23 provides a type-safe null pointer constant. Unlike NULL, nullptr has a distinct type and cannot be accidentally converted to integers.


#include <stdio.h>

int main() {
    int* ptr = nullptr;  // Type-safe null pointer
    
    if (ptr == nullptr) {
        printf("Pointer is null\n");
    }
    return 0;
}
                                    

nullptr Features

🔒

Type Safety

Cannot convert to integers

int x = nullptr; // Error!
🎯

Distinct Type

Has its own type: nullptr_t

nullptr_t null_val = nullptr;
🔄

Convertible

Converts to any pointer type

int* p = nullptr;
char* q = nullptr;
⚖️

Comparable

Can compare with pointers

if (ptr == nullptr) { }

🔹 nullptr vs NULL

nullptr is a type-safe null pointer constant, while NULL is a traditional macro often defined as 0. nullptr prevents subtle type confusion bugs where NULL matches integer overloads unintentionally. Modern C standards prefer nullptr for type safety and clarity. NULL remains widely used for compatibility with existing code. Understanding differences helps write safer code and appreciate language evolution toward stronger type safety in null pointer handling.

#include <stdio.h>
#include <stddef.h>

void func_int(int x) {
    printf("Called with int: %d\n", x);
}

void func_ptr(int* ptr) {
    printf("Called with pointer: %p\n", (void*)ptr);
}

int main() {
    // NULL can be ambiguous
    // func_int(NULL);    // This might work (NULL is often 0)
    // func_int(nullptr); // Error: cannot convert nullptr to int
    
    // Both work for pointers
    int* p1 = NULL;
    int* p2 = nullptr;
    
    printf("NULL pointer: %p\n", (void*)p1);
    printf("nullptr: %p\n", (void*)p2);
    
    // Type safety demonstration
    printf("sizeof(NULL): %zu\n", sizeof(NULL));
    printf("sizeof(nullptr): %zu\n", sizeof(nullptr));
    
    return 0;
}

Output:

NULL pointer: (nil)
nullptr: (nil)
sizeof(NULL): 8
sizeof(nullptr): 8

🔹 Using nullptr_t Type

The nullptr_t type represents the type of null pointer constants, enabling overload resolution. Functions can specifically accept nullptr_t parameters for null pointer-only overloads distinct from integers. This type enables powerful type-safe APIs preventing common null pointer handling mistakes. Understanding nullptr_t demonstrates advanced C features supporting modern programming patterns. Using these features creates robust code with compile-time safety guarantees.

#include <stdio.h>
#include <stddef.h>

// Function that accepts nullptr_t
void handle_null(nullptr_t null_val) {
    printf("Received nullptr_t value\n");
    // null_val is always nullptr
}

// Function overloading simulation with _Generic
#define process(x) _Generic((x), \
    nullptr_t: handle_null, \
    int*: handle_int_ptr, \
    char*: handle_char_ptr \
)(x)

void handle_int_ptr(int* ptr) {
    printf("Handling int pointer: %p\n", (void*)ptr);
}

void handle_char_ptr(char* ptr) {
    printf("Handling char pointer: %p\n", (void*)ptr);
}

int main() {
    nullptr_t my_null = nullptr;
    int* int_ptr = nullptr;
    char* char_ptr = nullptr;
    
    // Using the generic macro
    process(my_null);    // Calls handle_null
    process(int_ptr);    // Calls handle_int_ptr
    process(char_ptr);   // Calls handle_char_ptr
    
    return 0;
}

Output:

Received nullptr_t value
Handling int pointer: (nil)
Handling char pointer: (nil)

🔹 Practical Examples

The nullptr keyword in C is used to represent a null pointer value, providing a safer and more explicit way to initialize and check pointers. Unlike traditional NULL macro definitions, nullptr offers type safety and helps prevent common pointer-related bugs in modern C programming. For example, int *ptr = nullptr; clearly indicates that the pointer doesn't point to any valid memory location yet. This makes code more readable and helps developers quickly identify uninitialized pointers during debugging and code review processes.

#include <stdio.h>
#include <stdlib.h>

// Safe pointer checking function
bool is_valid_ptr(void* ptr) {
    return ptr != nullptr;
}

// Dynamic memory allocation with nullptr checking
int* create_array(size_t size) {
    if (size == 0) {
        return nullptr;  // Return nullptr for invalid size
    }
    
    int* arr = malloc(size * sizeof(int));
    if (arr == nullptr) {
        printf("Memory allocation failed\n");
        return nullptr;
    }
    
    // Initialize array
    for (size_t i = 0; i < size; i++) {
        arr[i] = (int)i;
    }
    
    return arr;
}

int main() {
    // Test with valid size
    int* array1 = create_array(5);
    if (array1 != nullptr) {
        printf("Array created successfully\n");
        for (int i = 0; i < 5; i++) {
            printf("array1[%d] = %d\n", i, array1[i]);
        }
        free(array1);
        array1 = nullptr;  // Set to nullptr after freeing
    }
    
    // Test with invalid size
    int* array2 = create_array(0);
    if (array2 == nullptr) {
        printf("Array creation failed as expected\n");
    }
    
    return 0;
}

Output:

Array created successfully
array1[0] = 0
array1[1] = 1
array1[2] = 2
array1[3] = 3
array1[4] = 4
Array creation failed as expected

🔹 Best Practices

Following best practices when using nullptr ensures robust and maintainable C code that minimizes pointer-related errors. Always initialize pointers to nullptr when declaring them if they won't immediately point to valid memory. Before dereferencing any pointer, check if it's nullptr to avoid segmentation faults and undefined behavior. Use nullptr instead of NULL or 0 for better code clarity and type safety. Additionally, set pointers to nullptr after freeing dynamically allocated memory to prevent dangling pointer issues that could lead to security vulnerabilities or program crashes.

nullptr Best Practices:

  • Use nullptr instead of NULL: For better type safety
  • Initialize pointers: Always initialize pointers to nullptr
  • Check before use: Always check if pointer != nullptr
  • Set after free: Set pointers to nullptr after freeing memory
  • Function parameters: Use nullptr_t for functions that only accept null
// Good practices with nullptr
#include <stdio.h>
#include <stdlib.h>

int main() {
    // Initialize to nullptr
    int* ptr = nullptr;
    
    // Allocate memory
    ptr = malloc(sizeof(int));
    if (ptr != nullptr) {
        *ptr = 42;
        printf("Value: %d\n", *ptr);
        
        // Free and nullify
        free(ptr);
        ptr = nullptr;  // Prevent dangling pointer
    }
    
    // Safe to check again
    if (ptr == nullptr) {
        printf("Pointer is safely null\n");
    }
    
    return 0;
}

🧠 Test Your Knowledge

What is the main advantage of nullptr over NULL?