“不安全代码”中的错误,使用指针读取内存

本文关键字:指针 内存 读取 错误 代码 不安全 不安全代码 | 更新日期: 2023-09-27 17:59:06

我在内存中有一个二进制序列化对象,我想通过使用C#中的指针(unsafae代码)从内存中读取它。请看下面的函数,它是从内存流中读取的。

static Results ReadUsingPointers(byte[] data)
{
    unsafe
    {
        fixed (byte* packet = &data[0])
        {
            return *(Results*)packet;
        }
    }
}

在这个return *(Results*)packet;语句中,我得到一个编译时异常"无法获取的地址、获取的大小或声明指向托管类型Results的指针"

这是我的结构

public struct Results
{
    public int Id;
    public int Score;
    public char[] Product;
}

根据我的理解,我的结构的所有属性都是可闪电传输的属性,那么我为什么会出现这个错误,如果我需要在结构中使用char[],我该怎么办?

编辑-1让我进一步解释一下(请注意,这些对象是模拟的)。。。

背景:我有一个Results对象数组,我使用二进制序列化对它们进行了序列化。现在,在程序的后期阶段,由于数据量很大,我需要尽快对内存中的数据进行反序列化。所以我在尝试,不安全的代码可以在那里帮助我。

比方说,如果我的结构不包括public char[] Product;,我会以相当好的速度返回数据。但对于char[],它会给我一个错误(编译器应该这样做)。我想找到一个在这种情况下使用char[]的解决方案。

“不安全代码”中的错误,使用指针读取内存

MSDN说:

以下任何类型都可以是指针类型:

  • sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal或bool。

  • 任何枚举类型。

  • 任何指针类型。

  • 仅包含非托管类型的字段的任何用户定义的结构类型

因此,可以如下定义结构以修复编译器错误:

public struct Results
{
    public int Id;
    public int Score;
    // Don't actually do this though.
    public unsafe char* Product;
}

通过这种方式,您可以指向数组的第一个元素。

然而,根据您编辑的问题,您需要一种不同的方法。

我有一个Results对象数组,我使用二进制序列化对它们进行了序列化。现在,在我的程序的后期阶段,我需要尽快在内存中反序列化中的数据

通常您会使用BinaryFormatter来实现此目的。如果这太慢,那么问题应该是是否可以从一开始就避免序列化。

你不能指望它会起作用。

public struct Results
{
    public int Id;
    public int Score;
    public char[] Product;
}

char[]阵列Product是托管类型。您的代码尝试使用类型Results*。这是一个指针类型。文档指出,您可以声明指向以下任何一个的指针:

  • sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal或bool
  • 任何枚举类型
  • 任何指针类型
  • 仅包含非托管类型字段的任何用户定义的结构类型

现在,您的结构显然与前三个项目符号都不匹配。并且也不匹配最终项目符号,因为数组是托管类型。

根据我的理解,我的结构的所有属性都是可闪电传输的属性。

是的,这是真的,但并不相关。您需要结构的成员多于blitable。


即使你的代码可以编译,你怎么能想象它能工作呢?考虑一下这个表达式:

*(Results*)packet

编译器如何将其转化为创建新数组并复制数组中正确数量的元素的东西?很明显,编译器没有希望在这里做任何有用的事情,这当然就是语言拒绝您的代码的原因。

我认为不安全的代码在这里对你没有帮助。序列化数组时,必须序列化长度,然后序列化数组的内容。要反序列化,您需要读取长度,创建一个具有该长度的新数组,然后读取内容。不安全的代码对此没有帮助。静态定义类型的简单内存副本是没有用的,因为这意味着数组的长度在编译时是已知的。事实并非如此。


关于你的更新,你说:

我有一个Results对象数组,我使用二进制序列化对其进行了序列化。

为了反序列化,您需要了解二进制序列化的详细布局的代码。问题中的代码做不到。

您可能还不明白的是,您不能期望复制任意的内存块,这些内存块的长度是可变的,只有在运行时才知道,而实际上却不知道这些长度。实际上,你希望能够在系统中不知道要复制多少的情况下复制内存。

使用不安全的类型转换和内存副本进行反序列化的尝试无法工作。如果不考虑序列化的二进制格式,就无法获得任何更详细的帮助。