我们如何检查指针是不是为 NULL 指针?

Posted

技术标签:

【中文标题】我们如何检查指针是不是为 NULL 指针?【英文标题】:How do we check if a pointer is NULL pointer?我们如何检查指针是否为 NULL 指针? 【发布时间】:2011-09-05 08:41:35 【问题描述】:

我一直认为只需 if(p != NULL).. 就可以完成这项工作。但是看了this Stack Overflow question,好像没有。

那么在吸收该问题中的所有讨论之后检查 NULL 指针的规范方法是什么,该问题说 NULL 指针可以具有非零值?

【问题讨论】:

那不是 c...这是一个 c++ 线程...就个人而言,我会选择:if(p) ... 您太担心了——您的代码很好,即使在 C++ 中也是如此。那次讨论是在一些语言律师之间进行的——有点像“多少天使可以在一根针头上跳舞”的东西。 @cpuer 不,他们不会,因为他们没有使用内部代表 - 你的代码很好!这是所有 C 代码和所有 C++ 代码的编写方式——该线程是关于 C++ 标准措辞的抽象智力讨论。你在 C++ 标签上得到了很多。 为了让问题更清晰:NULL 是一个宏,在<stddef.h>(和其他一些标头)中定义。 NULL 不是空指针;它需要被定义为“空指针常量”(在 C++ 中,它不能是指针,而在 C 中,传统上不是指针)。必须处理三个独立的概念:NULL、空指针和空指针常量。并且空指针的物理表示方式(其位模式)完全独立于其他两个。 【参考方案1】:

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

会的。

【讨论】:

这种风格被认为比 if(p) 更好,因为 if 语句中的表达式应该是一个布尔值,而在这种情况下“p”是一个指针,而不是一个布尔值。使用 == 或 != (MISRA-C:2004 13.2) 明确检查零被认为是更安全和更好的做法。 @Lundin:if 语句中的条件只需要可转换为布尔值即可;在那种情况下p 等同于p != NULL,这纯粹是您选择的美学问题。两者都不比另一个更安全或“更好的做法”。 @Mike Seymour 我更反对if (p),因为我仍然需要停下来考虑一下。正如您所说,这可能是经验问题;我只使用 C 和 C++ 30 年,所以毫无疑问它会及时出现。 (说真的,这可能是另一个经验的问题;在开始使用 C 之前,我广泛使用 Pascal 和 Modula-2,并且已经习惯了严格的类型。我从来没有对 C 的松散感到满意,到处都是隐式转换这个地方。) @James:好吧,我错了,说有经验的程序员不会发现任何一种形式都不清楚。这让我感到惊讶,但我无法与证据争论。 @Lundin:在 C 中,是的。在 C++ 中,(void*)0 不是NULL 的有效定义;它必须可转换为任何指针类型,并且 C++ 不允许从 void* 隐式转换为其他指针类型。所以NULL 必须定义为整数类型的零值文字,并且将其与另一个数字类型进行比较不会给出警告。 (C++0x 将通过引入 nullptr 来解决这个问题,它是一种可转换为指针类型但不能转换为数字类型的类型,但在那之前我们只需要尽可能地应付自如)。【参考方案2】:

首先,要 100% 清楚,C 和 C++ 之间没有区别 这里。其次,您引用的 Stack Overflow 问题并没有谈论空指针。它引入了无效指针;指针,至少就 就标准而言,仅通过尝试导致未定义的行为 比较它们。一般来说,没有办法测试指针是否 有效。

最后,有三种普遍的方法来检查空指针:

if ( p != NULL ) ...

if ( p != 0 ) ...

if ( p ) ...

所有工作,无论空指针在 机器。而且,所有这些都以某种方式具有误导性;哪一个你 选择是选择最不坏的问题。正式地,前两个 对于编译器是相同的;常量NULL0 被转换 指向p 类型的空指针,以及转换的结果 与p 进行比较。无论 null 的表示形式如何 指针。

第三个略有不同:p被隐式转换 到bool。但是隐式转换被定义为p != 0 的结果,所以你最终得到了同样的结果。 (这意味着有 使用第三种风格确实没有有效的论据——它混淆了 具有隐式转换,没有任何抵消的好处。)

你更喜欢前两个中的哪一个很大程度上取决于风格, 可能部分取决于您在其他地方的编程风格: 根据所涉及的成语,其中一个谎言会更麻烦 比另一个。如果只是一个比较的问题,我认为大多数 人们会喜欢NULL,但在类似f( NULL )的情况下, 将选择的重载是f( int ),而不是带有 指针。同样,如果f 是一个函数模板,f( NULL ) 将 在int 上实例化模板。 (当然,一些编译器,比如 g++,如果在非指针上下文中使用NULL,将生成警告; 如果你使用 g++,你真的应该使用NULL。)

在C++11,当然首选的成语是:

if ( p != nullptr ) ...

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

【讨论】:

@James Kanze,我认为没有void *p = main;if(p == 0x4004e3)printf("1\n");打印1这样的隐式转换(这里0x4004e3应该替换为main的实际地址)。也就是说,指针可以用来与整数进行比较,不涉及转换。 @compile-fan 当然没有这样的隐式转换。 main 实际上是一种非常特殊的类型;我不认为有任何方法可以获取它的地址,至少在 C++ 中是这样,如果可以的话,你也无能为力。但一般来说,没有一种指针类型到另一种类型的隐式转换,除非任何数据指针都可以转换为具有可接受的 cv 限定符的void*。如果您引用的代码编译,则编译器已损坏。 @James Kanze,它是用C编译的,我想也应该用c++编译。你可以把代码放到int main(int argc,char *argv[])...的正文中。 发出警告,但它仍然可以编译。 @vompile-fan 这不是合法的 C,也不是合法的 C++。在 C 语言中,您可以获取main 的地址,前提是main 的声明是可见的;在 C++ 中,我不确定。然而,在这两种语言中,都没有将指向函数的指针隐式转换为void*,并且除了空指针常量之外,您也不能将指针与整数进行比较。由于历史原因,第一个通常被接受(有或没有警告);然而,接受第二个的编译器被严重破坏了。【参考方案3】:

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

这意味着

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

不保证在所有目标系统上的行为都相同,因为您在此处对位模式做出假设。

例如,我有一个没有内存保护的嵌入式平台,并将中断向量保持在地址 0,因此按照惯例,整数和指针在转换时与 0x2000000 进行异或运算,从而使 (void *)0 指向取消引用时会产生总线错误的地址,但是使用 if 语句测试指针将首先将其返回为整数表示,然后是全零。

【讨论】:

OK,那么让我们将空指针 const(0,void *0,NULL) 视为一种特殊情况,将指针与非零整数进行比较时呢?请看我的上面更新的问题:) 您仍然需要转换任何一个值以便进行比较,没有直接的比较运算符。在我的编译器中,这意味着左侧或右侧在比较之前进行了异或运算,这使得整个事情再次保持一致。 这很明智,但不是必需的。将0 分配给int,然后将int 显式转换为指针,与将常量0 隐式转换为指针相比,允许给出不同的结果。 @James Kanze 作为将“不需要诊断”这句话视为挑战的人,我对这个想法很感兴趣。今晚的 Minecraft 会议开始了。【参考方案4】:

空指针的实际表示在这里是无关紧要的。值为零的整数文字(包括0NULL 的任何有效定义)可以转换为任何指针类型,给出一个空指针,无论实际表示形式如何。所以p != NULLp != 0p 都是对非空指针的有效测试。

如果你写了像p != reinterpret_cast<void*>(0) 这样扭曲的东西,你可能会遇到空指针的非零表示的问题,所以不要这样做。

虽然我刚刚注意到您的问题被标记为 C 和 C++。我的回答是指C++,其他语言可能不一样。您使用的是哪种语言?

【讨论】:

将指针与非零整数进行比较时会怎样?请参阅上面我更新的问题:) @compile-fan: 与非零整数的比较不应该编译,因为指针不能直接与整数比较,只能隐式转换零值整数文字到一个(空)指针。您可以强制它使用狡猾的演员进行编译,但是行为是未定义的。 (再次,我回答的是 C++,但我很确定答案在 C 中是一样的)。【参考方案5】:

显然你提到的线程是关于C++

C 中,您的 sn-p 将始终有效。我喜欢更简单的if (p) /* ... */

【讨论】:

@pmg,我添加了 c++ 标签,所以我的目的是总结出一种检查空指针的方法,该方法适用于 c/c++ @cpuer 做你正在做的事!真的,这里没有问题! 与多语言源文件所面临的问题相比,检查空指针算不了什么。我建议您坚持每个源文件使用一种语言。 ;) @pmg,当然,我永远不会在一个文件中混合两种语言:) 所以,当它是C时,使用if (p)if (p != NULL)if (p != 0))或if (!p)if (p == NULL)if (p == 0));当它是 C++ 时,使用 C++ 成语(我不知道它是什么)。【参考方案6】:

指针的表示与比较它们无关,因为 C 中的所有比较都是作为 而不是表示。比较表示的唯一方法是可怕的,例如:

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

【讨论】:

@R..,也许你可以就此多说几句:) 在我看来至少应该是!memcmp(ptr, ptr_rep, sizeof ptr)... 不,我的版本是正确的。你想比较ptr的表示,而不是它所指向的表示,所以你需要变量ptr的地址。 @R..,如果将指针与非零整数进行比较,会发生隐式转换吗?还是像@James Kanze 所说的那样,接受将指针与整数进行比较的编译器,而不是空指针常量,被严重破坏了? 指针不能与没有显式转换的整数进行比较,它具有实现定义的行为。 整数常量表达式 零(但不是非整数常量表达式零)恰好是特殊的;整数常量表达式 0 在需要时变为空指针。一个有趣的结果是void *dummy = sizeof(short)-2; 在编译时断言sizeof(short)==2(只有当表达式计算为 0 时它才是有效的 C)。 if (p != 0x567) 不是有效的 C 语言,不会编译。你的意思是if (p != (void *)0x567),但这具有实现定义的行为,不一定与比较表示相同。【参考方案7】:

嗯,这个问题早在 2011 年就被问到并得到了回答,但 C++11 中有 nullptr。这就是我目前使用的全部。

您可以阅读more from Stack Overflow 和this article。

【讨论】:

问题不排除 C++11。您可以通过以上两个链接了解更多信息。 我不知道你是想惹恼别人还是什么。我提供的链接给出了很多解释。在这里复制粘贴链接的内容是没有用的——而且会很糟糕。因为他的问题已经在 stackoverlow 中得到了回答。我通过说“您可以在 C++11 中使用 nullptr”来提供解决方案,同时提供详细说明的链接。如果我写了if(p == nullptr) 。在答案中,这只是对 OP 的侮辱。 nullptr IS 官方 C++ 标准中包含的规范方式。你的不礼貌不会再浪费我的时间了。【参考方案8】:

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 指针,保证 比较不等于指向任何对象或函数的指针。

虽然上述声明0(转换为指针时)和(void *)0 都构成一个空指针常量,但这并不意味着生成的指针的所有位都为 0。将值从一种类型转换为另一种类型的标准并不一定意味着表示是相同的。

这也表明空指针常量将与任何对象或函数比较不相等。本节第 4 段还指出:

将空指针转换为另一种指针类型会产生该类型的空指针。 任何两个空指针应该比较相等。

因此,如果p 是一个空指针,那么它必须与包括NULL 在内的任何空指针进行比较,在这种情况下p != NULL 将评估为假。相反,如果p 指向一个对象或函数,那么它必须与任何空指针进行比较,在这种情况下p != NULL 将评估为真。

再次注意,这里没有任何关于空指针的表示形式的假设。

【讨论】:

以上是关于我们如何检查指针是不是为 NULL 指针?的主要内容,如果未能解决你的问题,请参考以下文章

如何检查双指针中的空白空间(NULL)

如何检查无效指针?

如何检查接口是不是是指向切片的指针

为什么我们需要在C ++中检查空指针,而不是在Java中?

爪哇“?”用于检查 null 的运算符 - 它是啥? (不是三元!)

使用if条件检查指针是否为NULL时,C ++程序崩溃