网站首页 > 技术文章 正文
某些时候,我们需要将指针赋值为空指针,以防止野指针。
有人喜欢使用NULL作为空指针常量使用,例如:int* p = NULL;。
也有人直接使用0值作为空指针常量,例如:int* p = 0;。
前者可能觉得:NULL作为空指针常量,名字很形象,可读性较强。
后者可能觉得:NULL并不是C/C++语言的关键字,而是一个在标准库头文件<stddef.h>中定义的宏,因此要使用NULL,可能需要直接或简介地包含<stddef.h>头文件,比较麻烦。
问题一:NULL与常数0值有何区别?
要弄清楚这个问题,我们采用问与答的形式来描述。
问:NULL到底是什么?
答:NULL是一个宏。
问:它的值是多少?
答:C/C++标准规定:它的值是一个空指针常量(null pointer constant),由实现定义。#1,#2
问:什么样的值才能称之为空指针常量?
答:C语言中常数0和(void*)0都是空指针常量;C++中(暂且忽略C++11)常数0是,而(void*)0 不是。#3,#4
问:NULL宏是在哪里定义的?
答:通常是在C标准库的<stddef.h>头文件中,不过别的头文件中可能也有定义。
问:一般编译器的<stddef.h>头文件中NULL宏是如何定义的?
答:以gcc或clang编译器为例,NULL的定义大致如下(稍有简化):
#if defined(__cplusplus) # define NULL 0 // C++中使用0作为NULL的值 #else # define NULL ((void *)0) // C中使用((void *)0)作为NULL的值 #endif
问:为什么C中(void*)0是空指针常量,而C++中不是?
答:因为C语言中任何类型的指针都可以(隐式地)转换为void*型,反过来也行,而C++中void*型不能隐式地转换为别的类型指针(例如:int*p = (void*)0;使用C++编译器编译会报错)。#5,#6
问:既然C/C++标准中,常数0都可作为空指针常量,为什么不统一使用0?
答:个人觉得由于(void*)0更能体现指针的意义,而常数0更多的时候是用作整数。因此,C语言中NULL定义选择了(void*)0。(仅供参考)
问题二:C++11中为什么要引入nullptr?
考虑着这样一个函数重载的情形:
#include <stddef.h> void foo(int) {} // #1 void foo(char*) {} // #2 int main { foo(NULL); // 调用#1还是#2? }
从字面上来讲,NULL是个空指针常量,我们可能会觉得:既然是个指针,那么应该调用#2。但事实上调用的却是#1,因为C++中NULL扩展为常数0,它是int型。
根本原因就是:常数0既是整数常量,也是空指针常量。
为了解决这种二义性,C++11标准引入了关键字nullptr,它作为一种空指针常量。#7例如:
void foo(int) {} // #1 void foo(char*) {} // #2 int main { foo(nullptr); // 它会毫无异议地调用#2 }
附注:
[#1] C99: 7.17-p3:
The macros are
NULL
which expands to an implementation-defined null pointer constant; and ...
[#2] C++03: 18.1-p4:
The macro NULL is an implementation-defined C + + null pointer constant in this International Standard(4.10).
[#3] C99: 6.3.2.3-p3:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
[#4] C++03: 4.10-p1:
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.
[#5] C99: 6.3.2.3-p1:
A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
[#6] C++03: 4.10-p2:
An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue of type “pointer to cv void.”
[#7] C++11: 4.10-p1:
A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t.
参考:
(1) C99/C++03/C++11标准文档
猜你喜欢
- 2025-01-07 MACD追踪主力!超强MACD,让你一眼看穿主力动向!附指标公式
- 2025-01-07 Hive的10种常用优化总结,再也不怕MapReduce分配不均了
- 2025-01-07 使用C++编写的整蛊人的程序两例
- 2025-01-07 C语言中空指针和NULL关系是什么?
- 2025-01-07 垃圾回收器 G1 详解
- 2025-01-07 盘点c++几种常见的设计模式及具体实现
- 2025-01-07 10问10答:你真的了解线程池吗?
- 2025-01-07 EXPMA选股法
- 2025-01-07 无锁队列的几种实现及其性能对比
- 2025-01-07 满屏红色:C语言中最常见的这些报错,你经历过多少
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)