0

我有一个较旧的项目,它使用 fread() 将二进制文件读入结构。它使用 Visual Studio 2017 (v141)

我将项目升级到 VS 2019 的最新 C++ 工具版本 (v142),并将解决方案从 AnyCPU 更改为 x86。

我还将结构成员对齐方式从:1 字节 (/Zp1) 更改为:默认

因为这个错误:

错误 C2338 Windows 标头需要默认打包选项。更改此设置可能会导致内存损坏。可以通过定义 WINDOWS_IGNORE_PACKING_MISMATCH 来禁用此诊断。

但是现在数据不再被正确读取。

这是我用来读取二进制文件的代码:

FILE* RecipeFile;
    short ReturnValue = 0; //AOK

    if ((RecipeFile = fopen(rcpFile, "rb")) == NULL) {
        ReturnValue = -1; //File open error
        return(ReturnValue);
    }

    long sizeOfItem;

    // obtain file size:
    fseek(RecipeFile, 0, SEEK_END);
    sizeOfItem = ftell(RecipeFile); // gives 771088
    rewind(RecipeFile);

    //sizeOfItem = sizeof(recipe); // gives 824304??

    // now read the file contents:
    int noOfItemsRead = fread(&recipe, sizeOfItem, 1, RecipeFile);

recipe是一个结构。见下文。

我注意到这sizeof(recipe)给出了与文件大小不同的结果。使用工具集 141,我得到 798276 字节。使用工具集 142,我得到 824304 字节。实际文件大小为 771088 字节。

这个问题真的是由结构成员对齐的变化引起的吗?

如何修复错误,以便再次正确读取文件?

编辑:我尝试将编译指示包指令添加到结构中,如下所示:

#pragma pack(push, 1)
struct tRecipe {   
    RMPTableDescriptorType O2Flow;
    RMPTableDescriptorType HeFlow;
    RMPTableDescriptorType SiCl4Flow;
    RMPTableDescriptorType GeCl4Flow;
    RMPTableDescriptorType Extra_Flow;
    RMPTableDescriptorType POCl3Flow;
    RMPTableDescriptorType C2F6Flow;
    RMPTableDescriptorType SiF4Flow;
    RMPTableDescriptorType Cl2Flow;
    RMPTableDescriptorType BCl3Flow;
    RMPTableDescriptorType TTC_Temp;
    RMPTableDescriptorType TTC_H2Flow;
    RMPTableDescriptorType TTC_Ratio;
    RMPTableDescriptorType LCC_Speed;
    RMPTableDescriptorType LSC_Speed;
    RMPTableDescriptorType LTC_Speed;
    RMPTableDescriptorType TDS_Cursor;
    RMPTableDescriptorType TDC_Ctrl;
    RMPTableDescriptorType TPC_Ctrl;
    RMPTableDescriptorType TSS_Cursor;
    DLUTableDescriptorType DLU;
    EXHTableDescriptorType EXH;
    GENTableDescriptorType GEN;
    PARTableDescriptorType PAR;
    REFTableDescriptorType REF;
    SETTableDescriptorType SET;
    SUPTableDescriptorType SUP;
    TDCTableDescriptorType TDC;
    TDSTableDescriptorType TDS;
    TSSTableDescriptorType TSS;
    TTCTableDescriptorType TTC;
    TPCTableDescriptorType TPC;
};
#pragma pack(pop)

typedef struct
{
    RParam                      Value;
    UInt16                      Duration;
    OptType                     Opt;
#if DOS
    int                         Unused16BitVar; /* Established to allow Win32 NT Code to use 32bit */
#endif
}
RMPElementDescriptorType;

/*-----------------------------------------------------------------------------*/
typedef struct
{
    UInt16                      StartNoOf;
    UInt16                      EndNoOf;
    Char8                       StartRampType;
    Char8                       EndRampType;
    RMPElementDescriptorType    RMPElementArray[RAMP_ELEM_NO_OF];
}
RMPRampDescriptorType;

/*-----------------------------------------------------------------------------*/
typedef struct
{
    UInt16                 SizeInfo;
    Char8                  DBPTxtInfo[DBP_TXT_NO_OF];

    RMPRampDescriptorType  RMPRampArray[RAMP_NO_OF];
}
RMPTableDescriptorType;

但它似乎对错误没有任何影响。

4

1 回答 1

2

/Zp1总是一个坏主意——它在全球范围内应用包装。通过更改打包,您会使现有文件不兼容。相反,您应该有选择地打包:

#pragma pack(1) // 1 byte packing
struct sRecipe
{
   ...
} ;
#pragma pack() // restore default packing

但是,这可能无法解决编译器、编译器版本和目标之间的所有兼容性问题(并且您还将目标从 AnyCPU 更改为 x86)。实际上,最好使用 CSV、XML 或具有“网络字节顺序”(使用htonl()/nltoh() )的二进制文件对保存到文件的数据进行序列化/反序列化,以避免原始结构的对齐和字节顺序问题。

本质上,您应该编写代码以显式生成您为文件指定的格式,二进制文件的逐字节或使用明确的字符串表示。

于 2021-03-08T14:02:19.877 回答