C++ 从数组转换为结构并转换为 C#

Posted

技术标签:

【中文标题】C++ 从数组转换为结构并转换为 C#【英文标题】:C++ Casting from Array to Struct and Translate to C# 【发布时间】:2021-10-27 19:16:33 【问题描述】:

我有一个旧的 C++ 源代码来解析输入文件,我删除了一些不相关的部分并复制了它(逻辑相同),如下所示:

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <iostream>

typedef struct

    unsigned short int id;
    unsigned long size;
    unsigned long time;
    unsigned short int day;
    unsigned short int year;
    unsigned short int source;
    unsigned short int destination;
 header;

#define READ_SIZE    1024 * 2
#define MAX_LENGTH   1024
#define BUFFER_SIZE    READ_SIZE + MAX_LENGTH
const int SIZEOF_CHAR = sizeof(unsigned char);

int main()

    int index = 0;
    int offset = 0;
    header* hdrPtr;
    FILE* fp;
    char buff[BUFFER_SIZE];

    fp = fopen("data.dat", "rb");
    int numRead = fread(&buff[offset], SIZEOF_CHAR, READ_SIZE, fp);
    hdrPtr = (header*)(unsigned short*)&buff[index];

    return 0;

输入的 data.dat 文件是一个二进制文件,具有一定的格式/结构规则(老实说,此时我不确定这些规则是什么,我的工作是弄清楚它们,然后翻译成新的 C# .NET 代码库)。为了更容易,我用一些虚拟文本文件和一些随机二进制文件(如 .PDF 或 .MP4)进行了测试,但仍然得到了结果(hdrPtr)。但是,我仍然无法理解这些结果。

例如我测试了一个data.dat文本文件,内容为:

Hello
World

我得到了指向带有这些值的标题的指针(我不明白这些数字是如何产生的):

id          25928       unsigned short
size        1460276591  unsigned long
time        1684828783  unsigned long
day         52428       unsigned short
year        52428       unsigned short
source      52428       unsigned short
destination 52428       unsigned short

与其他输入文件 data.dat 相同,它应该是二进制文件。直到上周我才有使用 C++ 的经验,这对我来说似乎很奇怪!为什么我们可以将数组的第一个元素的地址转换为指向 unsighed short 的指针,然后转换为指向标头结构的指针(!?)。

我正在努力将上述代码转换为 C#。感谢任何帮助/提示和建议!

【问题讨论】:

从技术上讲,此代码是非法的。任何对象都可以视为char 的数组,但反之则不然。发生的事情是从文件中读取的位模式被视为一个结构,并且由于文件中的位模式不是结构,结果是疯狂的。 (header*) 是显式类型转换。大多数时候你应该避免这些,因为它们告诉编译器关闭它的大脑并完全按照你告诉它做的事情去做。您必须绝对确定自己是正确的,并且您必须实际上是正确的,因为如果您不正确,则不会有任何警告。无论运行时的结果如何,编译器都会生成完全按照您的要求执行的代码。当我看到其中一种转换时,我的经验法则是假设存在错误并更仔细地检查代码。 int numRead = fread(&amp;buff[offset], SIZEOF_CHAR, READ_SIZE, fp); 会比header* hdr; int numRead = fread(&amp;hdr, sizeof(hdr), 1, fp); 安全得多,但仍有一些问题。该文件可能不包含有效的标头,除了读取标头之外无法进行检查,检查numRead 以确保您读取了足够的字节,然后对读取的值进行健全性检查。数字中的byte order 可能是向后的。整数的大小可能与所写的不同。信不信由你,我见过 32 位 short @user 如果你要使用fread(&amp;hdr, sizeof(hdr), 1, fp);,那么header* hdr;应该是header hdr; 是的。上面的错误。 header* hdr; int numRead = fread(&amp;hdr, sizeof(hdr), 1, fp); 应该是header hdr; int numRead = fread(&amp;hdr, sizeof(hdr), 1, fp); 我从标识符中删除了ptr,但忽略了删除首先使变量成为指针的* 【参考方案1】:

这样的?

public struct Header

    public ushort id;
    public ulong size;
    public ulong time;
    public ushort day;
    public ushort year;
    public ushort source;
    public ushort destination;

    public static Header ReadHeader(FileStream fs, int index = 0)
    
        var br = new BinaryReader(fs);
        Header header = default(Header);
        int offset = Marshal.SizeOf(header) * index;
        br.ReadBytes(offset); // eat up bytes until header with index is reached
        header.id = br.ReadUInt16();
        header.size = br.ReadUInt64();
        header.time = br.ReadUInt64();
        header.day = br.ReadUInt16();
        header.year = br.ReadUInt16();
        header.source = br.ReadUInt16();
        header.destination = br.ReadUInt16();
        return header;
    


class Program

    static void Main(string[] args)
    
        var fs = File.OpenRead("data.dat");
        int index = 0;
        var header = Header.ReadHeader(fs, index);
        
        Debug.WriteLine($"ID=header.id, Size=header.size");
    

【讨论】:

我的 C# 几乎不存在,所以我要问:是否考虑了字节序? 我假设 ReadUInt64() 背后的魔力以正确的字节顺序读取文件,但我可能错了。 我看不出它是怎么做到的,因为它不知道文件中数字的字节顺序。 @PaulSanders ReadUInt64() 假设数字仅以小端序存储。所以如果它存储在大端,你会遇到麻烦。 c-sharpcorner.com/UploadFile/mahesh/…

以上是关于C++ 从数组转换为结构并转换为 C#的主要内容,如果未能解决你的问题,请参考以下文章

c++ 结构指针转换为数组互操作 c#

如何将 C++ 结构转换为 C# 结构

BitBlt 转换为字节数组并从 c++ 解析为 c#

将结构列表从 c# 转换为 c++

将包含结构的 c++ 函数传递给 C#,并使用 Marshal.PtrToStructure 将其转换为 c# 函数

在构建时将 C++ 结构转换为 C# 结构