【问题标题】:Null reference exception vs zero pointer access violation空引用异常与零指针访问冲突
【发布时间】:2014-04-09 11:39:41
【问题描述】:

最近我读了一本关于 c# 的书,并且对当指针被取消引用时空引用异常与本机代码中的访问冲突不同的说法感到非常困惑。这句话的意思我不太清楚,能解释一下吗?

【问题讨论】:

  • 当您说 in native code 时,您的意思实际上是指在托管代码中吗?在本机代码中访问您的进程没有读/写访问权限的一块内存(除 null 外)也将生成一个 AV。托管代码通常不会发生这种情况。
  • 很遗憾,我无法准确回答。书中有“在本机代码中”,但我认为这等于“在非托管代码中”。

标签: c# nullreferenceexception null-pointer


【解决方案1】:

在本机代码中,您可以使用指针直接访问内存位置。如果指针是 32 位,您可以访问大约 40 亿个虚拟内存位置 (2^32)。但是,并非所有虚拟内存位置都映射到物理内存,并且某些位置仅映射为只读(例如代码)。如果本机代码尝试访问无法访问的虚拟内存位置,则会在 CPU 上发生访问冲突。

因此,当您使用无效指针(读取或写入指针指向的位置)时,会发生访问冲突。硬件内存管理器的保护通过捕获 CPU 上的无效访问并引发某种形式的错误条件或异常来帮助您发现这些错误。但是,您也可能拥有导致访问可访问内存位置的无效指针。即使访问无效,CPU 也不会发现它,这可能导致内存损坏和其他难以修复的错误。

本机代码中的一个常见错误是忘记在使用指针之前对其进行初始化。然后指针通常会指向地址 0(也称为空指针)。捕获此类错误的一种策略是使虚拟地址空间中的第一页(从地址 0 开始)不可访问。当无效指针被取消引用时,您会遇到访问冲突。因此,在本机代码中,由于地址 0 无法访问,因此会将空指针错误报告为访问冲突。

.NET 在虚拟机上执行并且不提供指针(除非您编写 unsafe 代码)。访问冲突的概念不适用于 .NET 代码。但是,.NET 引用可以未初始化(例如 null)。取消引用空引用时,虚拟机将抛出NullReferenceException。从概念上讲,这有点类似于如上所述将本机代码空指针错误报告为访问冲突的方式,但概念和机制不同。

【讨论】:

  • 非常感谢,您能不能只澄清两点:1. AccessViolationException 是在 CPU 级别(之前)捕获的,而 NullReferenceException 是在虚拟机级别捕获的,是吗?
  • 2. “但是,您也可能拥有无效的指针,导致访问可访问的内存位置。” - 你不能在这里澄清一下“无效指针”是什么意思吗?
  • @EvgeniyKluchikov:在 C 中,您可以 malloc 一个缓冲区,然后 free 它。如果您在调用free 后保留该指针,则它是一个无效指针,因为您不应该再使用它。如果您稍后写入指针的位置,您就犯了一个错误,堆可能会因为您覆盖重要数据而损坏。但是,您不会遇到访问冲突,因为您的代码可以完全访问该位置,但是当您的程序以意外方式崩溃时,该错误可能会在稍后出现。因此,所有无效指针错误都不会以访问冲突的形式出现。
  • @EvgeniyKluchikov:回复:AccessViolationException。这是一个 .NET 异常,因为 .NET 调用的某些本机代码可能会导致访问冲突。这将转换为AccessViolationException,但在托管代码中,您不应期待此异常。 访问冲突是一个适用于具有内存管理的计算机的概念。这是一项硬件功能,由依赖于 CPU 的特殊机制报告。在 Intel x86 上,该机制是一个中断。在 Windows 上,这被转换为结构化异常,然后在 .NET 中被转换为AccessViolationException
  • 谢谢,好像很有意思。你不能推荐一本关于计算机体系结构的好书,而不是更多地了解这些和其他有趣的事情吗?
【解决方案2】:

当您取消引用 null 的对象引用时,会发生 NullReferenceExceptionnull 是 CLR 上的一个特殊值,表示“无对象”。它确实 not 表示空指针。实际上它是一个空指针,但这是一个实现细节。

对于托管代码,AccessViolationException 没有意义,因为在托管代码中没有指针。不要将概念级别与当前实现混淆。

【讨论】:

  • 即这只是 当前 实现的概念差异?
  • 不,这是永久性的,因为概念模型和物理实现是两件事。 CLI 规范从不谈论实现细节(除了可能非常罕见,但我不知道有任何这样的地方)。如果这两个模型恰好紧密对齐(就像现在一样),那是巧合。
  • 您可能有兴趣知道 nullref 异常确实是通过取消引用空指针来实现的。然后 CLR 捕获访问冲突并将其转换为 NullReferenceException。但是所有这些对托管代码都是不可见的。
  • 非常感谢,是的,这对我来说很有趣。
猜你喜欢
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-17
  • 1970-01-01
相关资源
最近更新 更多