【发布时间】:2012-01-28 16:05:24
【问题描述】:
我正在使用不安全代码解决Code Golf, 上的一个问题,但我发现了一些我无法解释的东西。这段代码:
unsafe
{
int i = *(int*)0;
}
由于访问冲突(Segfault)而崩溃,但这段代码:
unsafe
{
*(int*)0=0;
}
抛出 NullReferenceException。在我看来,第一个执行读取,第二个执行写入。一个异常告诉我,CLR 中的某处正在拦截写入并在操作系统终止进程之前停止写入。为什么在写入时会发生这种情况,而在读取时不会发生这种情况?如果我使指针值足够大,它会在写入时出现段错误。这是否意味着 CLR 知道有一块内存是保留的,甚至不会尝试写入?那么,为什么它允许我尝试从该块中读取?我在这里完全误解了什么吗?
编辑:
有趣的是:System.Runtime.InteropServices.Marshal.WriteInt32(IntPtr.Zero, 0); 给了我访问冲突,而不是 NullReference。
【问题讨论】:
-
在我的机器上都抛出一个
NullReferenceException。 -
推测:第二个抛出 NullReferenceException 是因为编译器能够确定这是它总是会做的事情,并用 throw 语句替换了异常。
-
@MikeNakis:我向你保证,编译器并没有那么复杂。
-
两个 sn-ps 代码都会导致非托管 AccessViolation 异常(异常代码 0xc0000005),然后由 .NET 异常处理代码转换为 NullReferenceException,因为访问发生在虚拟内存地址的较低 64KB空间。很难猜出为什么您在第一次 sn-p 时没有获得 NRE。您将获得带有
*(int*)-1的AVE。更多关于这里的信息:stackoverflow.com/a/7940659/17034 -
@EricLippert 哦,好的。我想我现在可以认为自己拥有良好的权威。