什么是“空指针赋值错误”?

关于C指针的求职面试问题之一如下: 什么是空指针赋值错误?

我用谷歌搜索了一段时间,没有看到任何合理的解释。 那是什么? 试图通过空指针写? 某种架构或环境特定的东西? 究竟是什么错误?

http://www.faqs.org/qa/qa-3786.html

NULL指针赋值是一个运行时错误它由于各种原因而发生,一个是您的程序试图访问非法内存位置。 非法位置意味着位置位于操作系统地址空间或其他进程内存空间中。 在stdio.h中,NULL定义为0因此,只要程序尝试访问第0个位置,操作系统就会因运行时分配错误而导致程序死亡,因为第0个位置位于操作系统地址空间中,操作系统不允许访问其地址用户程序的空间。

示例代码:

int* ptr = NULL; *ptr = 3; 

说明:
在几乎每个系统上,地址0都被保留。 系统不允许您写入该位置。 如果您尝试,您将获得运行时exception(访问冲突, 分段错误等)。

我实际上不记得源,但根据源,这个运行时错误仅限于相应编译器使用的中小型内存模型。 你看,如前所述,空指针实际上并不指向零,实际上不同的编译器使用不同但固定的内存位置来用作空指针。

让我们考虑TC编译器的情况,这个编译器在数据段的底部放置四个零字节和TC版权声明。 TC还使用DS:0000位置,数据段底部为空指针位置。 因此,为这个空指针赋值,将会实际改变四个字节,并可能搞乱版权声明。

现在,在程序终止时,将检查四个零和版权标题以进行任何类型的更改。 如果发现任何更改,则会生成空指针分配错误。

所以,我认为它不仅仅是空指针,任何狂野的指针,如果试图访问某些关键区域,你会遇到空指针赋值错误。

在许多情况下,您可以看到问题。 但关键是,你没有正确分配内存。 运行程序后,以下代码将生成Null指针赋值错误消息。 注意:它将正确编译。

 void CopyMessage(char *p) { strcpy(p, "welcome"); } void main() { char *src; CopyMessage(src); } 

当您尝试指向非法内存空间(通常是为OS保留的地址0)时,会发生运行时错误。

2018-10-15:重写

我在这个答案中的意图是对空指针的最基本概念的补充。 许多地方列出的这个最简单的定义是指向地址的基值被赋值为’0’或NULL值,因为零页面,第0个内存位置是操作系统地址空间的一部分,操作系统不允许用户程序访问其地址空间。 在这种情况下,预编译器或编译器可能会生成错误,或者操作系统本身在运行时可能会因内存访问冲突而生成错误。

以下对空指针的讨论基于编程中包含的概念,这些概念发生在允许精确控制的语言的机器级别,并且需要了解如何处理变量空间。

大多数高级语言和编译器可以通过适当的“类型”转换来防止这种情况发生,并指定一个选项库,并且在索引编制时不会产生精神错误估计。 “C”作为一种语言,没有指定最严格的编译器参数,特别容易出现这些类型的错误,以及现在在“口袋”处理器中发现的不太复杂的基于机器的编译器或编程语言。

但是,自计算机和编程语言开始以来,零指针错误的概念已扩展为包括指向任何受保护存储器位置中的第0个位置的任何指针。 但尤其是在可以用于指向任何内存位置的内存位置如何被无意中覆盖以包含空值的上下文中。 在这里,我检查这个概念,因为我称之为“1的错误”,当程序员必须在选项库’0’或选项库’1’之间切换时发生。 这是一个计数问题,我们以“0”或“1”开始计数,如下所示:

 Option Base 0 [0,1,2,..,9] or Option Base 1 [1,2,3,...,10] 

对于包含10个元素的数组。 错误1可能会导致错误计算,从而导致指向arrays之前的第一个内存位置,

 Option Base 1 0[1,2,3,...,10] ^ |Last memory location of another variable space 'before' this variable space 

或者根据定义超出界限的数组之后的第一个位置。

 Option Base 0 [0,1,2,...,9]10 ^ |First memory location of another variable after this variable 

但是,当处理使用直接内存访问的编程时,如在任何语言的原始机器代码中,1的错误可能是悲剧性的,将非预期值放在预期范围之外的存储位置,在可变空间和使用的情况下指针是预期变量之前或之后的变量空间,当数组初始化或清除时,会在不需要的位置创建“null”或0,特别是如果它是指针数组,则在非预期变量中出现空指针错误。 这当然取决于可变空间的结构和/或类型。 如果变量或其他存储地址空间嵌套在代码中,则会特别麻烦。 正如我先前所述,许多高级语言编译器可以绕过大多数此类错误; 但是,当在机器代码中编写特定子程序时,无论出于何种原因认为必要,必须特别注意确保选项库明确定义并且如果不是编译器约定则通过实践遵守。

首先,程序员认识到程序和存储区域都是明确定义的必要性,没有明确的同意,任何东西都不应该修改一个数据位。 这对于空指针是至关重要的,因为在操作系统的零页面区域中的第0个存储器位置经常用于存储堆栈,该堆栈是被推入堆栈以进行返回操作的存储器位置。 系统调用是否由于可屏蔽或不可屏蔽的中断而推送返回操作的地址(从系统中断的位置弹出返回地址),或者因为程序员想要推送数据或内存位置稍后弹出这个堆栈。 这是一个保护区。 像任何指向有效内存地址的指针一样,人们不希望写入错误的位置,如果被覆盖,则第0个位置特别容易受到影响,因为变量通常为空或者从初始上电状态的值为0,因此变量上电后未明确定义或已故意初始化的可能为零。

如果堆栈在零页面上,或任何包含返回地址的堆栈,如果值被压入堆栈,并且在遇到’return’之前没有弹出,则返回值可以为null或零,返回值指向堆栈的内存位置。 这是一个空指针错误,它可能不会生成错误,但会将代码指针返回到不包含代码的区域,例如堆栈的中间位置。 这些漏洞利用众所周知,常常用于破坏系统安全性的方法,以便不那么严格的破解者获取访问权限; 或者可以用于特殊情况下的巧妙访问,或者当意外造成难以确定来源的各种恶作剧时。

正如我所说,这个描述超出了空指针错误的传统定义,但它仍然可以产生一个空指针,尽管更常产生其他错误,或者根本没有。 除了“如果或何时”程序未能按预期执行时,它通常不会指示其存在。

在这里,我提供了额外的非常规示例和空指针赋值错误的潜在来源的定义,而不是定义传统理解,这在编程约定中比编程逻辑中的错误更多是错误。

这种类型的错误(未定义或为null)非常罕见。 但现代的“口袋”处理器编程,使用像Arduino,Raspberry PI,AMD或任何其他计算机芯片编程这样的台式设备存在于无数种forms中,其中许多forms今天都像过去一样简单,这个空指针问题至今仍然存在,即使在最复杂的系统中也可能发生。 此外,构建自己的变量或数据结构的公司可能也是现在最容易看到这种类型错误的人。 目的是展示可以帮助识别的例子。

正如在旧时所定义的那样,很快就认识到产生空指针错误的条件也可能产生错误,其中指针的值被无意修改。 然后,作为一个变量,它被用作指针并且在没有程序员的意图或知识的情况下被覆盖,这可能是空的,但也可以是任何其他值。 因此,我们发现可以创建空指针的问题也可以创建非空指针。 空指针是一种特殊情况,OFTEN创建系统错误消息; 但是,当这些相同的条件导致指针采用随机或未确定的值而不是数据应驻留的原始地址时,它现在包含一个空或未知地址,导致数据移动或存储到无效或不需要的位置覆盖和破坏代码或数据。

大多数人都会理所当然地认为 不是空指针错误; 而且,它们完全100% 正确 ! 但是,此错误的根源通常会创建奇怪的常见空指针错误,因为指针通常会包含“null”! 这个练习的定义是指出如何创建空指针也会导致一个似乎没有问题原始来源的问题。 IOW,在概念上没有指向问题的指针。 由于与奇数’null’指针问题的关系的关系,并且在这种情况下随后缺少指向错误源的数据,因为指针是非null而是“未定义”的旧定时器已从转换过来自顶向下编程到面向对象的事件驱动编程识别这种关系并识别这种类型的“空”指向错误,它似乎没有可定义的源。

由于此类型失败,损坏的数据或损坏的代码可能无法在将其移动到现有未使用的内存位置时立即执行或使用。 但是,当代码或数据在运行的稍后时间确实导致问题时,没有关于错误的“真实”位置的信息,因为它与导致错误的事件的时间相距甚远。 无论是创建还是分配空指针还是创建其他一些损坏,它都会修改代码,事情会变得怪异,真的很奇怪。

总而言之,我将空指针定义为用于指向内存位置的任何空或未定义地址,而不管最初创建它的位置。 可以为此问题和示例分配空指针分配错误或许多其他错误。

在更简单的体系结构或编程环境中,它可以引用无意中最终创建空值作为指针的任何代码,或者创建一个无论如何都会停止执行的错误,比如覆盖返回堆栈中的一个字节,覆盖代码,意外存储的代码错误位置,现有代码或错误位置的数据中的“0”,而不仅仅是地址。

因此,虽然上面的示例,可以正常工作来定义空指针的示例。 所以我们扩展了这个概念,空指针是任何被用作变量指针的指针,并且该变量的地址位置由于多种原因中的任何一个现在包含一个’null’或任何非预期的值,导致它指向一个不希望的内存位置,无论它如何到达,不仅仅是编程逻辑错误或数学计算错误。 IOW,不仅仅是指针中的0; 更具体地说,在内存位置不是特定目标的任何内存位置中的零或未定义值,并且在其他情况下具有其现在将执行的其他目的!

所以,最后,一个人可以得到一个空指针错误,并在检查指针时发现它包含一个null; 但是,找不到将null放入指针或分配它的代码。 这是空指针赋值错误的最广泛定义,绝对是空指针错误最坏情况。 当在大型程序中发生这种情况时,它通常会导致程序死亡,因为如果错误存在于以前的版本中,而是写入以前可访问但未分配的非预期内存位置(允许程序运行或IOW)在早期版本中,错误在程序扩展之前不被注意,现在曾经以前未使用的内存位置包含新代码或数据,这允许旧错误现在在新代码中生成随机错误或破坏数据!

例如:在早期版本中,错误的地址值会导致数据被写入定义的变量空间之外,但是由于正在写入数据并且正在读取数据并且程序和所有内容“出现”,因此几个版本都没有注意到这些数据好! 但是,随着程序的扩展,新代码现在存在于相同的相对地址空间中,而原始旧错误错误地写入错误的内存位置并且没有人注意到的内存,无论是一个字节还是整个数据块! 但是,现在,那里存在新的代码。 当程序运行该特定代码时,无论今天或明天调用包含它的任何函数,新数据都会被旧的未发现的错误破坏。

找到一年前存在的“原始”错误现在几乎(如果不是完全的话)找不到。

管理员和开发人员逻辑通常要求,我为什么要在那里看,我们知道代码运行并且在最后几个版本中运行得很好。 但是,现在,新代码的一部分不起作用,主要部分被打破。 我们看,看,没有找到任何东西。 这就好像错误不存在,但它确实存在。 造成这种情况的原因是谁怀疑多年前编写的代码? 空引导和许多其他错误也是由此引起的。 通过理解和可以直接检查代码的良好编辑器,适当的监视器可以监视修改的内存位置,触发停止以确定正确执行的代码,甚至可以找到。

也许这个答案是错误的,并且可以更好地列在其他地方。 主持人? 也许有人担心,因为这个“答案”并不合适,因为在问题中使用“赋值”这个词有些担心,这意味着故意分配。 但是,转让的概念不需要知识或同意。 算法或代码可以赋值。 可以通过多种方式创建空指针,这里的示例可能是传统理解导致错误的原因之外的最常见指针,这种错误似乎无处可寻。 我个人已经看到这样的错误会杀死产品的整个行。 最终来自一群用户的喧嚣与他们最喜欢的产品中不固定的虫子的持续存在感到不安; 旧产品报废了,一个新的产品从头开始创建……

看来这个信息有’a’的地方,这里作为答案包含在内。 积极的富有成效的反馈或建议是值得赞赏的,而不是简单的投票,如果有人关心评论,也许元讨论是有序的?

干杯。