C++的零指针(NULL,0,nullptr)

本文介绍了零指针从C语言中的NULL到C++11中nullptr的发展历程。详细解释了NULL在C++中的定义和使用问题,并对比了NULL与0在函数重载中的差异。最后推荐在现代C++中使用nullptr关键字。

原文地址:http://www.cppblog.com/airtrack/archive/2012/09/16/190828.aspx

零指针的定义

C语言:NULL
C++03前:0
C++11:nullptr

进化之路

最开始,C语言中的NULL通常定义成

#define NULL ((void *)0)
// C语言有隐式指针转换,可以写如下代码
int *i = NULL;
time_t* t = NULL;

然而C++语言要求更为严格,不允许进行这样的隐式转换,而是直接使用字面值0作为零指针,为了兼容之前的习惯,NULL会进行这样的定义

#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif

不过当时C++的书籍都推荐用0而不是NULL,这是为什么呢?我所参考的原文描述的原因是这样的:
因为C++有函数重载,考虑如下代码

void foo( int a, int* b );
// 其它文件中调用如下代码
foo( 1, NULL );
foo( 1, 0 );

调用正常,如果我们添加了一个重载函数

void foo( int a, int* p );
void foo( int a, int b );
// 其它文件中调用如下代码
foo( 1, NULL );
foo( 1, 0 );

对于NULL版本,我们可能无法意识到错误,因为我们一直会认为NULL就应该调用指针版本,而对于0来说,我们很快能够发现调用了整数版本,从而调整我们的调用方式:

foo( 1, static_cast<int*>(0) );

可是,我自己的实验结果是这样的

void f( int a, int* p );
foo( 1, NULL );  // ok
foo( 1, 0 );   // ok
void f( int a, int* p );
void f( int a, int b );
foo( 1, NULL );  // compile error, ambiguous
foo( 1, 0 );   // ok, but call f(int, int)

也就是在这种情况下,NULL和0并不等价,NULL版本直接给了编译错误,而0版本直接偷偷迁移到新的函数,我们可以得知,NULL和0并不是完全等价,目前来看,NULL比0似乎有了一点优势。
让我们看看NULL到底是怎么定义的,一般NULL定义在stddef.h里,我找到了gcc 4.9.1中是这么定义的:

/* A null pointer constant.  */

#if defined (_STDDEF_H) || defined (__need_NULL)
#undef NULL             /* in case <stdio.h> has defined it. */
#ifdef __GNUG__
#define NULL __null
#else   /* G++ */
#ifndef __cplusplus
#define NULL ((void *)0)
#else   /* C++ */
#define NULL 0
#endif  /* C++ */
#endif  /* G++ */
#endif  /* NULL not defined and <stddef.h> or need NULL.  */
#undef  __need_NULL

可以看到除了一个__null,其余部分和上面介绍的是一致的,所以g++编译器一定是用了__null,其实,这个__null是在gcc编译器源码中定义的,具体查找过程请见参考条目[1],我本想将其源码也贴过来的,后来觉得也没有必要,因为看了源码也不能立刻明白是怎么回事。相关源码文件是:

  • gcc/c-family/c-common.h
  • gcc/c-family/c-common.c
  • gcc/ginclude/stddef.h
  • gcc/cp/parser.c

也就是说,聪明的编译器会在遇到__null的时候,做特殊语法分析,从而让NULL表现的和整数0能够兼容,又不完全一样。在NULL当成0使用的时候,尽可能的给出警告,例如

void foo( int a, int b ); // 只有这个定义
foo( 1, NULL ); // g++会给出警告

编译器会尽可能提醒用户NULL应该作为指针,类似的,clang编译器也有一个__null的定义。

不管怎么样,以上的方案都是让人迷茫的,也都是不彻底的,所以C++11推出了nullptr关键字来表示零指针,它是一个C++语言的关键字,所以不要问是在哪里定义的,它的身份已经和NULL不同了。另外它的这个类型是std::nullptr_t,可以在stddef.h中发现这样的代码

namespace std { typedef decltype(nullptr) nullptr_t; }

我们应该在未来的代码中尽量多的使用nullptr,目前,C++代码库中的代码大部分还是使用的NULL。

参考
[1] http://blog.chinaunix.net/uid-20682147-id-4219051.html

<think>我们正在处理一个关于C语言中如何检查指针是否为空的问题。根据用户的问题,我们需要提供在C语言中判断指针是否为NULL的方法。 在C语言中,指针为空通常意味着指针不指向任何有效的内存地址。我们使用NULL宏来表示空指针,它通常定义0(void*)0。 检查指针是否为空的方法非常简单:直接与NULL进行比较。 例如:if (ptr == NULL) { ... } 或者 if (ptr != NULL) { ... } 另外,由于NULL在C语言中就是0,所以有时候也会直接写成:if (!ptr) 或 if (ptr) 的形式,但为了代码清晰,通常建议显式地与NULL比较。 参考用户提供的引用[1]中,有类似的处理:在fclose()前判断文件指针是否为空指针,使用了if(fp == NULL)。 因此,我们可以这样回答: 在C语言中,检查指针是否为空的方法是使用关系运算符与NULL进行比较。例如: if (pointer == NULL) { // 指针为空时的处理 } 或者: if (pointer != NULL) { // 指针非空时的处理 } 注意:在C语言中,NULL一个标准宏定义,通常在<stdio.h>、<stdlib.h>等头文件中定义。 下面我们给出一个简单的示例代码: ```c #include <stdio.h> int main() { int *ptr = NULL; // 将指针初始化为空 // 检查指针是否为空 if (ptr == NULL) { printf("指针为空\n"); } else { printf("指针不为空\n"); } return 0; } ``` 运行这段代码将输出“指针为空”,因为ptr被显式地初始化为NULL。 另外,用户提供的引用[3]中也有一个判断文件指针是否为空的例子,但更主要的是判断文件是否为空(即文件内容长度是否为0)。这里我们需要注意区分“指针为空”和“指针指向的内容为空”是两个不同的概念。 根据用户的问题,我们只关注指针本身是否为空(即是否为NULL),而不关心指针指向的内存内容。 因此,我们总结:在C语言中,判断指针是否为空,就是判断指针的值是否为NULL。 下面,我们生成相关问题: 1. 在C语言中,NULL指针和未初始化的指针有什么区别? 2. 如何避免在C语言中使用空指针? 3. C语言中空指针有什么用途? 注意:用户提供的引用中,引用[1]是关于文件指针的空指针判断,引用[2]是关于回文字符串的检查(与指针空判断无关),引用[3]是关于判断文件内容是否为空(不是指针为空)。所以我们的回答主要基于引用[1]中提到的空指针判断方法,并结合C语言的基本知识。</think>### C语言检查指针是否为空的方法 在C语言中,检查指针是否为空(即指向`NULL`)是防止程序崩溃的关键操作。以下是常用的方法和最佳实践: #### 1. **直接比较法(推荐)** ```c if (ptr == NULL) { // 处理指针为空的情况 printf("错误:指针为空!\n"); } ``` 或 ```c if (ptr != NULL) { // 安全使用指针 *ptr = 10; } ``` - **说明**:`NULL`是标准库定义的宏(通常为`(void*)0`),直接比较可明确表达意图[^1]。 #### 2. **隐式布尔判断** ```c if (!ptr) { // 等价于 ptr == NULL // 空指针处理 } if (ptr) { // 等价于 ptr != NULL // 非空指针操作 } ``` - **注意**:虽然简洁,但显式比较`ptr == NULL`更易读且不易出错。 #### 3. **函数参数检查** ```c void safe_print(int* ptr) { if (ptr == NULL) { fprintf(stderr, "无效指针\n"); return; } printf("%d\n", *ptr); } ``` - **关键点**:在解引用指针(如`*ptr`)前必须检查,否则可能引发段错误[^1]。 #### 4. **文件操作示例** ```c FILE* fp = fopen("file.txt", "r"); if (fp == NULL) { // 必须检查! perror("文件打开失败"); exit(EXIT_FAILURE); } // 安全使用fp... fclose(fp); ``` - **典型错误**:未检查`fopen()`返回的指针是否为空直接操作会导致崩溃[^1]。 #### 5. **动态内存检查** ```c int* arr = malloc(100 * sizeof(int)); if (arr == NULL) { // 处理内存分配失败 exit(EXIT_FAILURE); } // 安全使用arr... free(arr); ``` ### 为什么必须检查空指针? - **解引用空指针**:会导致未定义行为(通常是程序崩溃)。 - **函数安全**:如`free(NULL)`是安全的,但`fclose(NULL)`可能崩溃[^1]。 - **防御性编程**:避免因无效输入或资源不足导致程序终止。 > **最佳实践**:在指针来源不可靠时(如用户输入、动态分配、外部函数返回),**必须**进行空指针检查。显式使用`ptr == NULL`比隐式`if(!ptr)`更推荐,以提高代码可读性[^1][^3]。 --- ### 相关问题 1. `free(NULL)`在C语言中是否安全?为什么? 2. 如何区分指针未初始化和指针为空的不同风险? 3. 在C++中检查空指针的方法与C语言有何异同? [^1]: C语言文件操作中关于空指针的警告和处理方法 [^3]: C语言判断文件是否为空的代码示例
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值