我正在使用不安全的代码来解决Code Golf上的问题,我发现了一些我无法解释的问题.这段代码:
unsafe
{
int i = *(int*)0;
}
Run Code Online (Sandbox Code Playgroud)
与访问冲突(Segfault)崩溃,但此代码:
unsafe
{
*(int*)0=0;
}
Run Code Online (Sandbox Code Playgroud)
抛出NullReferenceException.在我看来,第一个是执行读取,第二个是执行写入.一个例外告诉我,在操作系统杀死进程之前,CLR中的某个地方正在拦截写入并停止写入.为什么这会在写入时发生,而不是在读取时发生?如果我使指针值足够大,它会在写入时发生段错误.这是否意味着CLR知道的内存块是保留的,甚至不会尝试写入?那么,为什么它允许我尝试从该块读取?我在这里完全误解了什么吗?
有趣的是:System.Runtime.InteropServices.Marshal.WriteInt32(IntPtr.Zero, 0);给我一个访问冲突,而不是NullReference.
当然,第一个例外是有道理的——你试图从内存地址 0 读取。第二个更有趣。:P 在 C++ 中,有一个名为 NULL 的宏/常量,其值为 0。它用于无效的指针地址 - 很像 C# 中引用类型的空值。由于 C# 引用是内部指针,因此当您尝试从该地址读取/写入时会发生 NullReferenceException - 地址 NULL 或 0;实际上,0 到 64K 的地址在所有进程中(在 Windows 中)都是无效的,以便捕捉程序员的错误。确切的异常或错误可能因计算机硬件或 Windows/.NET Framework 版本而略有不同,但您应该会收到两个代码片段的错误。
至于读/写随机地址时的段错误,那是由于操作系统对每个进程的隔离。你不能摆弄其他进程的代码或数据——至少不合法。