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

4
这个行为在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;
    }
}

3
把它编译并查看。这总是找出答案的最佳方法,问在这里也没有意义... - thecoop
为什么要这样做?请不要尝试在C#中编写C代码! - Krizz
6
编译只能告诉你代码在语法上是否正确,而不能告诉你代码在语义上是否正确。在这种特殊情况下,即使运行代码也未必能发现代码的缺陷。 - Brian
5个回答

18
如果你打算关闭安全系统,那么你就需要确保程序的内存安全,一旦你这样做了,就必须在没有安全系统帮助的情况下安全进行所有操作。这就是“不安全”的含义。
正如C#规范明确指出的那样:“只有使用fixed语句才能获取可移动变量的地址,并且该地址仅在该fixed语句的持续时间内有效。”
你正在获取可移动变量的地址,然后在fixed语句的持续时间过后使用它,因此该地址不再有效。因此,你特别被要求不要做你正在做的事情。
在你深入理解必须遵循的规则之前,不应编写任何不安全的代码。开始阅读规范的第18章。

你错了。你可以使用GCHandle结构体,通过使用pinned标志将对象固定到其内存地址,直到调用handle.Free()为止。 - Florian
6
@thefiloe: 我知道这一点。那与此相关吗?你认为我哪句话是错的? - Eric Lippert

11

这段代码可以编译通过,但是会导致运行时问题。实际上,该代码将指向堆中未修复对象的指针“走私”出去了。下一个移动MyClass类型的垃圾回收将同时移动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;

上一行代码是否导致应用程序崩溃,覆盖了另一种类型的字符串值,或者只是将一个值插入未初始化的数组并在其他地方搞乱了数学问题?你不知道。最好的结果是代码很快就会崩溃,但很可能会做出更微妙和邪恶的事情。


2

1

这段代码不能正常运行(虽然能够编译,但在运行时会导致问题)。一旦fixed区域结束,数据就不再固定。


0

不,一旦您离开fixed块,result的值将不再有效(如果GC没有运行,则可能巧合地有效)。

执行此类操作的正确方法是要么在未托管的内存中具有对byte[]的引用,通过C#代码访问它,要么将托管数组复制到未托管的内存中。


3
你的意思是说"fixeb block"而不是"unsafe block"吗? - JaredPar
@JaredPar - 不,fixed和unsafe是两个不同的关键字。你当然可以将一个unsafe变量设置为fixed。 - Security Hound
4
@Ramhound 是的,但是一个不安全的块并不能以任何方式保护值。即使它当前在一个不安全的块中,this对象也可以自由地在GC堆中移动。只有 fixed 块/引用才能防止在堆内部移动。 - JaredPar
3
杰瑞德是正确的。当控制离开“fixed块”而不是“unsafe块”时,该地址变得无效。请参阅C#规范的第18章。 - Eric Lippert
@JaredPar:是的,那是我犯的一个低级错误,本来想写fixed,结果写成了unsafe。>< - Guvante

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接