出于教育目的,我正在编写一组导致 C# 中运行时异常的方法,以了解所有异常是什么以及导致它们的原因。现在,我正在修改程序,导致AccessViolationException
.
(对我来说)最明显的方法是写入受保护的内存位置,如下所示:
System.Runtime.InteropServices.Marshal.WriteInt32(IntPtr.Zero, 0);
正如我所希望的,这引发了AccessViolationException
。我想做得更简洁,所以我决定用不安全的代码编写一个程序,并通过分配来做(我认为的)完全相同的事情0
到零指针。
unsafe
{
*(int*)0 = 0;
}
由于我无法理解的原因,这引发了NullReferenceException
。我玩了一些,发现使用*(int*)1
相反,还会抛出一个NullReferenceException
,但如果你使用负数,比如*(int*)-1
它会抛出一个AccessViolationException
.
这里发生了什么?为什么*(int*)0 = 0
导致NullReferenceException
,为什么它不会导致AccessViolationException
?
当您取消引用空指针时,会发生空引用异常; CLR 不关心空指针是插入整数零的不安全指针还是插入零的托管指针(即对引用类型对象的引用)。
CLR 如何知道 null 已被取消引用? CLR 如何知道其他无效指针何时被取消引用?每个指针都指向进程虚拟内存地址空间中虚拟内存页中的某个位置。操作系统会跟踪哪些页面有效,哪些页面无效;当您触摸无效页面时,它会引发 CLR 检测到的异常。然后,CLR 将其显示为无效访问异常或空引用异常。
如果无效访问内存底部 64K,则为空引用异常。否则为无效访问异常。
这解释了为什么取消引用 0 和 1 会产生空引用异常,以及为什么取消引用 -1 会产生无效访问异常; -1 是 32 位机器上的指针 0xFFFFFFFF,并且该特定页面(在 x86 机器上)始终保留给操作系统用于其自身目的。用户代码无法访问它。
现在,您可能会合理地问,为什么不只对指针零执行空引用异常,并对其他所有内容执行无效访问异常?因为大多数情况下,少量数字被取消引用是因为您通过空引用获得了它。例如,想象一下您尝试这样做:
int* p = (int*)0;
int x = p[1];
编译器将其翻译为道德上的等价物:
int* p = (int*)0;
int x = *( (int*)((int)p + 1 * sizeof(int)));
这是解除引用 4。但从用户的角度来看,p[1]
看起来确实像是对 null 的取消引用!这就是报告的错误。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)