2

我一直致力于将旧的 C++ 应用程序移植到 C#,但我不确定我的解决方案是否真的可行。

最令人头疼的是该应用程序的文件格式基本上只是一些(相当大的)结构转储到文件中。有趣的部分是格式要求整数以大端顺序排列,无论系统如何。因此,我拼凑了一种实用方法来翻转字节顺序,并使用大量固定大小的缓冲区构建了复制 C++ 布局的结构。此时导入就像将文件复制到内存并使用Marshal.PtrToStructure一样简单,这比手动读取要容易得多,但不会让我在构建时翻转字节顺序。

因此,格式在很大程度上取决于短变量,无论是单独的还是固定缓冲区中的。在将许多固定语句组合在一起以固定缓冲区以进行翻转的过程中,我有一个令人不安的想法。

除非我弄错了,否则只固定一个缓冲区会导致整个结构被固定(将结构的一部分从其余部分重新定位是没有意义的)。所以,我尝试过这样的事情:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct Example
{
    short value1;
    fixed short value2[10];
    short value3;
    fixed short value4[20];

    public void FlipEndianness()
    {
        fixed (short* ptr = &value1)
        {
            Utility.FlipShorts(ptr, 32);
        }
    }
}

这个想法是该实用程序方法适用于指针和计数,就像它是一个数组一样遍历指针 - 我最初编写它是为了翻转缓冲区,但我在这里看到了值。从理论上讲,它似乎工作正常 - 所有变量都在一次调用中翻转,没有那么大惊小怪。我需要知道的是我的概念是否真的安全。手动固定所有东西将是一个巨大的痛苦——我提到过这些结构是巨大的吗?

固定一个变量是否会固定整个结构并使这种诡计成为可能,或者到目前为止我只是幸运地没有把内存弄得一团糟?到目前为止,我的测试结果是肯定的,但我不能肯定地说。我宁愿不要让我受到这种打击。

4

1 回答 1

1

据我所知,没有任何资源可以告诉您在 C# 中这样做是安全的。但是你会侥幸逃脱,GC 中没有任何机制可以保持 struct 的字段被固定并仍然允许 struct 中的其他字段移动。像这样的固定语句会创建一个固定的内部指针,GC 足够聪明,可以从中发现对象根。内部指针只在 C++/CLI 的文献中被提及,这是一种允许直接用interior_ptr<>关键词。请注意,这在另一个 CLR 实现(如 Mono)上不一定能很好地工作。YMMV。C++/CLI 是更好的武器,至少您可以使用原始的 C 或 C++ 声明,而无需在 C# 中重写它。您将拥有一个指向本机结构的稳定指针。

首先这不应该是一个问题,只有当结构是在 GC 堆上分配的引用类型的一部分时才需要实际的对象固定。不知道您的代码是什么样的,但一种理智的方法是将这样的结构作为局部变量保留在读取和转换数据的方法中。此类值类型在堆栈上分配并具有稳定的地址。

请注意,这段代码中有很多不安全的地方,编译器和运行时都不能对你错误地使用魔法 32 值做任何事情。优化文件数据转换代码很少有用,读取数据的成本比解释它高几个数量级。如此之多,以至于使用反射来查找字段是合理的,你永远不会弄错。Jon Skeet 的 EndianBinaryReader 也应该被提及。

于 2014-03-11T11:40:18.997 回答