在C或C ++中执行“空检查”是什么意思?


21

我一直在学习C ++,并且很难理解null。特别是,我阅读的教程提到进行“空检查”,但是我不确定这意味着什么或为什么有必要。

  • 到底是什么?
  • “检查为空”是什么意思?
  • 我是否总是需要检查null?

任何代码示例将不胜感激。



我会建议得到一些更好的教程,如果你读讲null检查所有的人而没有解释他们,并提供示例代码...
underscore_d

Answers:


26

在C和C ++中,指针本质上是不安全的,也就是说,当您取消引用指针时,确保指针指向有效的位置是您的责任。这是“手动内存管理”的一部分(与Java,PHP或.NET运行时等语言中实现的自动内存管理方案相对,后者不允许您花费大量精力创建无效的引用)。

捕获许多错误的常见解决方案是将所有不指向任何内容的指针设置为NULL(或,在正确的C ++中0),并在访问指针之前对其进行检查。具体而言,通常的做法是所有的指针初始化为NULL(除非你已经拥有的东西,当你宣布他们在指向他们),并将它们设置为NULL,当你deletefree()他人(除非他们后立即去的范围了)。示例(在C语言中,也是有效的C ++语言):

void fill_foo(int* foo) {
    *foo = 23; // this will crash and burn if foo is NULL
}

更好的版本:

void fill_foo(int* foo) {
    if (!foo) { // this is the NULL check
        printf("This is wrong\n");
        return;
    }
    *foo = 23;
}

如果没有空检查,则将NULL指针传递给此函数将导致段错误,并且您无能为力-操作系统将简单地杀死您的进程,并可能会进行核心转储或弹出崩溃报告对话框。使用null检查后,您可以执行适当的错误处理并优雅地恢复-自己纠正问题,中止当前操作,编写日志条目,并在适当时通知用户。


3
@MrLister是什么意思,空检查在C ++中不起作用?您只需在声明它时将指针初始化为null即可。
TZHX 2012年

1
我的意思是,您必须记住将指针设置为NULL,否则它将不起作用。而且,如果您还记得,换句话说,如果您知道指针为NULL,则无论如何都不需要调用fill_foo。fill_foo检查指针是否具有值,而不是指针是否具有有效值。在C ++中,不能保证指针为NULL或具有有效值。
李斯特先生,2012年

4
assert()将是一个更好的解决方案。试图“安全”毫无意义。如果传入NULL,那显然是错误的,那么为什么不直接崩溃以使程序员完全意识到呢?(在生产中,这没关系,因为您已经证明没有人会用NULL调用fill_foo(),对吗?真的,这并不难。)
Ambroz Bizjak 2012年

7
别忘了提到此函数的更好版本应该使用引用而不是指针,从而使NULL检查变得过时。
布朗

4
这与手动内存管理无关,如果尝试取消引用空引用,托管程序也会崩溃(或至少引发异常,就像大多数语言中的本机程序一样)。
梅森惠勒2012年

7

其他答案几乎涵盖了您的确切问题。进行空检查以确保您收到的指针实际上指向类型的有效实例(对象,基元等)。

不过,我将在此处添加我自己的建议。避免空检查。:)空检查(以及其他形式的防御性编程)使代码混乱,实际上使它比其他错误处理技术更容易出错。

关于对象指针,我最喜欢的技术是使用Null Object模式。这意味着返回一个(指针,或者甚至更好地引用)一个空数组或列表而不是null,或者返回一个空字符串(“”)而不是null,甚至是字符串“ 0”(或等同于“ nothing”的东西) (在上下文中)。您希望将其解析为整数。

另外,您可能还不了解空指针,这是1965年CAR Hoare为Algol W语言首次实现的。

我称之为我的十亿美元错误。这是在1965年发明空引用的。那时,我正在设计第一个全面的类型系统,用于面向对象语言(ALGOL W)的引用。我的目标是确保所有引用的使用都绝对安全,并由编译器自动执行检查。但是我忍不住要插入一个空引用的诱惑,仅仅是因为它是如此容易实现。这导致了无数的错误,漏洞和系统崩溃,在最近四十年中可能造成十亿美元的痛苦和破坏。


6
空对象甚至比仅具有空指针还要糟糕。如果算法X要求您没有的数据Y,则这是程序中错误,您可以通过假装自己来隐藏它们。
DeadMG

这取决于上下文,在我的书中,对“数据存在性”的测试要比对空值的测试要好。根据我的经验,如果某个算法在一个列表上工作,而该列表为空,则该算法完全无事可做,仅通过使用标准控制语句(例如for / foreach)即可实现。
Yam Marcovic 2012年

如果算法无关,那么为什么还要调用它呢?首先,您可能想调用它的原因是因为它做了一些重要的事情
DeadMG 2012年

@DeadMG因为程序与输入有关,所以在现实世界中,与作业分配不同,输入可能无关紧要(例如,为空)。仍然以任何一种方式调用代码。您有两种选择:要么检查相关性(或为空),要么设计算法,以使它们在不使用条件语句显式检查相关性的情况下就能读取并正常工作。
Yam Marcovic 2012年

我来这里发表几乎相同的评论,所以改为给我投票。但是,我还要补充一点,这代表了一个更大的僵尸对象问题-每当您具有带有多阶段初始化(或销毁)的对象时,它们并没有完全存活但还没有完全消失。当您看到没有确定性终结语的语言中的“安全”代码,并在每个函数中添加了检查以查看对象是否已被处置时,这就是抬起头的一般问题。您永远不要if-null,而应该使用具有其生命周期所需对象的状态。
ex0du5 2012年

4

空指针值表示定义明确的“无处”;它是一个无效的指针值,可以保证将其与其他任何指针值进行比较。尝试取消引用空指针会导致未定义的行为,并且通常会导致运行时错误,因此您需要在尝试取消引用之前确保指针不是NULL。许多C和C ++库函数将返回空指针以指示错误情况。例如,如果库函数malloc无法分配已请求的字节数,则该库函数将返回空指针值,并且尝试通过该指针访问内存(通常)通常会导致运行时错误:

int *p = malloc(sizeof *p * N);
p[0] = ...; // this will (usually) blow up if malloc returned NULL

因此,我们需要malloc通过p对照NULL 的值来确保调用成功:

int *p = malloc(sizeof *p * N);
if (p != NULL) // or just if (p)
  p[0] = ...;

现在,请稍等片刻,这会有些颠簸。

有一个空指针和一个空指针常量,并且两者不一定相同。空指针是基础体系结构用来表示“无处”的任何值。此值可以是0x00000000、0xFFFFFFFF,0xDEADBEEF或完全不同的值。不要假定空指针始终为0。

空指针常量 OTOH始终是一个0值的整数表达式。就您的源代码而言,0(或任何等于0的整数表达式)表示空指针。C和C ++都将NULL宏定义为空指针常量。编译代码时,空指针常量将在生成的机器代码中替换为适当的空指针

另外,请注意,NULL只是许多可能的无效指针值之一;如果您声明一个自动指针变量而没有显式初始化它,例如

int *p;

最初存储在变量中的值是不确定的,并且可能不对应于有效或可访问的内存地址。不幸的是,在尝试使用非NULL指针值之前,没有(便携式)方法可以判断该值是否有效。因此,如果要处理指针,通常最好在声明它们时将它们显式初始化为NULL,并在它们未主动指向任何对象时将它们设置为NULL。

注意,与C ++相比,这在C语言中更多的是问题。惯用的C ++不应过多使用指针。


3

有两种方法,基本上都可以做同样的事情。

int * foo = NULL; //有时设置为0x00或0或0L而不是NULL

空检查(检查指针是否为空),版本A

if(foo == NULL)

空检查,版本B

if(!foo)//因为NULL被定义为0,所以!foo将从空指针返回一个值

空检查,版本C

if(foo == 0)

在这三个中,我更喜欢使用第一个检查,因为它明确告诉未来的开发人员您要检查的内容,并且可以清楚地表明您期望foo为指针。


2

你不知道 在C ++中使用指针的唯一原因是因为您明确希望存在空指针。否则,您可以参考,这在语义上既易于使用,又保证非null。


1
@James:内核模式下的“ new”?
Nemanja Trifunovic

1
@James:C ++的一种实现,代表了大多数C ++编码人员所享有的功能。其中包括所有 C ++ 03语言功能(除外export)和所有C ++ 03库功能以及 TR1 大量C ++ 11。
DeadMG 2012年

5
确实希望人们不会说“引用保证非空”。他们没有。生成空引用和空指针一样容易,并且它们以相同的方式传播。
mjfgates 2012年

2
@Stargazer:当您按照语言设计者和良好实践的建议使用工具时,问题是100%冗余。
DeadMG 2012年

2
@DeadMG,它是否多余无关紧要。你没有回答这个问题。我再说一遍:-1。
riwalk 2012年

-1

如果您不检查NULL值,特别是如果它是指向结构的指针,则可能遇到了安全漏洞-NULL指针取消引用。NULL指针取消引用可能导致其他一些严重的安全漏洞,例如缓冲区溢出,竞争条件...,这些漏洞可能允许攻击者控制您的计算机。

许多软件供应商,例如Microsoft,Oracle,Adobe,Apple ...发行了软件补丁来修复这些安全漏洞。我认为您应该检查每个指针的NULL值:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.