C语言 我们如何检查一个指针是否为空指针?

rpppsulh  于 2022-12-03  发布在  其他
关注(0)|答案(9)|浏览(1126)

我一直认为简单的if(p != NULL){..}就能完成这项工作。但是阅读this Stack Overflow question之后,似乎不行。
那么,在吸收了这个问题中的所有讨论之后,检查NULL指针的规范方法是什么呢?

oxcyiej7

oxcyiej71#

我总是简单地认为if(p!= NULL){..}将完成这项工作。
会的。

rjee0c15

rjee0c152#

首先,要100%明确地说,这里C和C++之间 * 没有 * 区别。它引入了无效指针;指针,至少就标准而言,仅仅试图比较它们就会导致未定义的行为。通常没有办法测试指针是否有效。
最后,有三种广泛使用的方法来检查空指针:

if ( p != NULL ) ...

if ( p != 0 ) ...

if ( p ) ...

所有的工作,不管机器上的空指针的表示。而且所有的,在某种程度上,都是误导性的;你选择哪一个是一个选择最不坏的问题。形式上,前两个对编译器是相同的;将常量NULL0转换为p类型的空指针,并将转换结果与p进行比较。
第三个稍有不同:p被隐式转换为bool。但是隐式转换被定义为p != 0的结果,所以结果是一样的。(这意味着使用第三种样式实际上没有有效的参数-它使用隐式转换进行混淆,没有任何补偿好处。)
您更喜欢前两个中的哪一个在很大程度上是一个风格问题,可能部分取决于您在其他地方的编程风格:根据所涉及的习惯用法,其中一个谎言会比另一个更麻烦。如果只是比较的问题,我想大多数人会喜欢NULL,但在像f( NULL )这样的东西中,将选择的重载是f( int ),而不是带有指针的重载。类似地,如果f是一个函数模板,f( NULL )将在int上示例化模板。(当然,如果在非指针上下文中使用NULL,某些编译器(如g++)将生成警告;如果您使用g++,则实际上应该使用NULL。)
当然,在C++11中,首选的习语是:

if ( p != nullptr ) ...

,这避免了其他解决方案的大多数问题。(但它与C不兼容:-)。)

7fyelxc5

7fyelxc53#

编译器必须提供一个一致的类型系统,并提供一组标准转换。整数值0和NULL指针都不需要用全零位表示,但编译器必须注意将输入文件中的“0”标记转换为整数零的正确表示,并且向指针类型的强制转换必须从整数转换为指针表示。
这意味着

void *p;
memset(&p, 0, sizeof p);
if(p) { ... }

并不保证在所有目标系统上的行为都相同,因为您在此处对位模式进行了假设。
例如,我有一个嵌入式平台,它没有内存保护,并将中断向量保存在地址0,因此按照惯例,整数和指针在转换时与0x2000000进行异或运算,这使得(void *)0指向在解引用时生成总线错误的地址,但是用if语句测试指针将首先将其返回到整数表示,其然后是全零。

6tdlim6h

6tdlim6h4#

空指针的实际表示在这里是无关紧要的。一个值为零的整型文字(包括0NULL的任何有效定义)可以被转换成任何指针类型,给出一个空指针,无论实际表示是什么。所以p != NULLp != 0p都是对非空指针的有效测试。
如果你写了一些像p != reinterpret_cast<void*>(0)这样的扭曲的东西,你可能会遇到空指针的非零表示的问题,所以不要这样做。
虽然我刚刚注意到你的问题被标记为C和C++。我的答案是指C++,其他语言可能不同。你使用的是哪种语言?

pvabu6sv

pvabu6sv5#

显然你提到的线程大约是C++
C中,你的代码片段总是可以工作的。我喜欢更简单的if (p) { /* ... */ }

guykilcj

guykilcj6#

指针的表示与指针的比较无关,因为C语言中所有的比较都是以而不是表示的形式进行的。比较指针表示的唯一方法是类似下面这样的可怕的东西:

static const char ptr_rep[sizeof ptr] = { 0 };
if (!memcmp(&ptr, ptr_rep, sizeof ptr)) ...
gev0vcfq

gev0vcfq7#

这个问题早在2011年就被提出并得到了回答,但是C++11中有nullptr,这就是我目前使用的全部。
您可以读取more from Stack Overflow,也可以从this article读取。

zy1mlcev

zy1mlcev8#

if(p != NULL)是一种安全且可移植的检查指针是否为NULL的方法。
C11 standard的7.19节描述了stddef. h中包含的定义,包括NULL,相关部分如下:

  • 1* 头文件<stddef.h>定义了以下宏并声明了以下类型。一些宏也在其他头文件中定义,如它们各自的子子句所示。

...

  • 3* 宏为
NULL

其扩展为实现定义的空指针常量;...
这只是说明NULL是由实现定义的,并没有说明它必须所有的位都为0。
此外,第6.2.3.2p3节定义了空指针和空指针常量:
值为0的整数常数运算式,或转换成void *型别的运算式,称为 *null指标常数 *。如果将null指标常数转换成指标型别,则产生的指标(称为 *null指标 *)保证会与任何对象或函式的指标不相等。
虽然上面声明0(当转换为指针时)和(void *)0都构成空指针常量,但这并不意味着结果指针的所有位都是0。标准中还有其他几个示例,其中将值从一种类型转换为另一种类型并不一定意味着表示形式相同。
这也说明了一个空指针常量与任何对象或函数相比都是不相等的。这一节的第4段也说明了:
将空指针转换为另一个指针类型会产生该类型的空指针。任何两个空指针都应该相等。
因此,如果p是空指针,则它必须与包括NULL在内的任何空指针进行比较,在这种情况下,p != NULL将评估为假。相反,如果p指向对象或函数,则它必须与任何空指针进行比较,在这种情况下,p != NULL将评估为真。
再次注意,这里没有对空指针的表示做任何假设。

qltillow

qltillow9#

现在,你可以使用is nullis not null结构,它们读起来很清楚。

if (ptr is null)
    throw new ArgumentNullException();

if (ptr is not null)
{
    // do something
}

相关问题