获取指向字节数组的指针的不安全方法

本文关键字:指针 不安全 方法 数组 字节数 取指 字节 获取 | 更新日期: 2023-09-27 18:30:35

此行为在 C# 中是否有效

public class MyClass
{
    private byte[] data;
    public MyClass()
    {
        this.data = new byte[1024];
    }
    public unsafe byte* getData()
    {
        byte* result = null;
        fixed (byte* dataPtr = data)
        {
            result = dataPtr;
        }
        return result;
    }
}

获取指向字节数组的指针的不安全方法

如果您要关闭安全系统,那么您有责任确保程序的内存安全。一旦你这样做,你就需要在没有安全系统帮助你的情况下安全地做所有事情。这就是"不安全"的意思。

正如 C# 规范明确指出的那样:

可移动变量的地址只能使用 fixed 语句获取,并且该地址仅在该固定语句的持续时间内保持有效。

您正在获取可移动变量的地址,然后在固定语句的持续时间之后使用它,因此该地址不再有效。因此,您被特别要求不要完全按照您正在做的事情去做

在您对必须遵循的规则有透彻和深入的了解之前,您不应该编写任何不安全的代码。首先阅读规范的第 18 章。

这段代码可以很好地编译,但它会导致运行时问题。 代码实质上是偷运出指向堆中未固定对象的指针。 移动MyClass类型的下一个 GC 也将随之移动data引用,以前从 getData 返回的任何值现在都将指向不正确的位置。

var obj = new MyClass();
unsafe byte* pValue = obj.getData();
// Assuming no GC has happened (bad assumption) then this works fine
*pValue = 42;
// Assume a GC has now happened and `obj` moved around in the heap.  The 
// following code is now over writing memory it simply doesn't own
*pValue = 42;

最后一行是否导致应用程序崩溃,覆盖另一种类型的string值,或者只是将值戳到未初始化的数组中,然后在其他地方搞砸数学问题? 你不知道。 最好的结果是代码只是很快崩溃,但在所有可能的引擎盖上,它会做一些更微妙和邪恶的事情。

您可以使用

Marshal.StructureToPtr()方法代替不安全的魔法:)

StructureToPtr将结构的内容复制到预分配 PTR 参数指向的内存块。

Marshal.StructureToPtr Method (Object, IntPtr, Boolean)

此代码将不起作用(它将编译,但在运行时会导致问题)。固定区域结束后,不再固定数据。

不,一旦您离开fixed块,result 的值就不再有效(如果 GC 尚未运行,它可能恰好有效)。

执行此类操作的正确方法是引用通过 C# 代码访问的非托管内存中的byte[],或者将托管数组复制到非托管内存中。