C 语言中,NULL、0 和 (void *)0 经常被用来表示空指针,通常推荐使用 NULL,因为它提高了代码的可读性。当明确需要类型为 void * 的空指针时,使用 (void *)0。如果仅在局部上下文中使用空指针,0 也可以,但应小心避免歧义。

1、NULL

NULL 是在 或 中定义的宏,通常被定义为 ((void *)0) 或 0。它是用来表示空指针的标准符号,用于提高代码的可读性。是一个宏,而不是关键字,通常定义为 (void *)0(确保类型为 void *),在指针上下文中被用作空指针。

#include <stdio.h>

void printPointerValue(int *ptr) {
    if (ptr == NULL) {
        printf("The pointer is NULL.\n");
    } else {
        printf("Pointer value: %d\n", *ptr);
    }
}

int main() {
    int *p = NULL; // 未初始化的指针通常设置为 NULL
    printPointerValue(p);

    int value = 10;
    p = &value;
    printPointerValue(p);

    return 0;
}

2、0

字面量 0 是一个整数常量,但在 C 和 C++ 中也可以用作空指针常量。在指针上下文中,0 会被隐式转换为适当类型的空指针。使用 0 是合法的,但相比 NULL 可读性较差。

#include <stdio.h>

int main() {
    int *ptr = 0; // 显式将指针设置为0
    if (ptr == 0) {
        printf("The pointer is null.\n");
    }
    return 0;
}

3、(void *)0

C 中,(void *)0 将整数 0 显式地转换为 void * 类型,这是 void * 的空指针常量。这也是 NULL 在 C 中的典型定义方式。(void *)0 是 C 中有效的空指针常量。在 C++ 中,(void *)0 不能隐式转换为其他指针类型,因为 C++ 对类型转换要求更严格。这也是 C++11 引入 nullptr 的原因之一。

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

int main() {
    void *ptr = (void *)0; // 初始化为空指针

    ptr = malloc(10 * sizeof(int)); // 动态分配内存
    if (ptr == (void *)0) {         // 检查是否分配成功
        printf("Memory allocation failed!\n");
        return 1;
    }

    // 将通用指针转换为具体类型后使用
    int *intArray = (int *)ptr;
    for (int i = 0; i < 10; i++) {
        intArray[i] = i + 1;
        printf("%d ", intArray[i]);
    }
    printf("\n");

    free(ptr); // 释放内存
    ptr = (void *)0; // 将指针重置为空指针
    return 0;
}

4、区别

C 语言中,空指针常量的宏通常定义为 (void *)0,并可以作为空指针常量的整数使用,明确表示为空指针。它在指针上下文中是类型安全的,能提高代码的可读性。

对于 C++,空指针常量通常定义为 0,并且可以转换为空指针,但在 C++ 中不能隐式转换为其他指针类型,这使得其类型安全性相对较低。虽然它可以合法地使用,但由于缺乏明确的类型信息,可能不如 nullptr 或显式的 void * 安全。

因此,在 C 语言中,使用 (void *)0 可以提升代码的可读性,而在 C++ 中,则应尽量避免使用这种形式,建议仅在需要显式表示 void * 类型的空指针时使用。

#include <stdio.h>

#define NULL (void *)0  // 定义 NULL 为 (void *)0

int main() {
    int* int_ptr = NULL;        // 使用 NULL
    int* int_ptr2 = 0;          // 使用 0
    void* void_ptr = (void *)0; // 使用 (void *)0

    printf("int_ptr: %p\n", (void*)int_ptr);
    printf("int_ptr2: %p\n", (void*)int_ptr2);
    printf("void_ptr: %p\n", void_ptr);

    // 检查三者是否相等
    if (int_ptr == int_ptr2) {
        printf("int_ptr and int_ptr2 are equal.\n");
    }

    if (int_ptr == void_ptr) {
        printf("int_ptr and void_ptr are equal.\n");
    }

    return 0;
}

5、 C++ 中的 nullptr

在 C++11 及以上版本中,引入了 nullptr 作为空指针常量。nullptr 是强类型的,解决了与 NULL0 相关的歧义问题。

#include <iostream>
using namespace std;

int main() {
    int* ptr = nullptr;  // 将指针初始化为空指针
    if (ptr == nullptr) {
        cout << "ptr is a null pointer." << endl;
    }
    return 0;
}