【问题标题】:fread() not reading struct data correctly after upgrading C++ tools version升级 C++ 工具版本后 fread() 无法正确读取结构数据
【发布时间】:2021-03-08 12:36:48
【问题描述】:

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

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

我还更改了 Struct Member 对齐方式: 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;

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

【问题讨论】:

  • 你应该把你的结构打包。见stackoverflow.com/questions/1537964/…
  • MSVC 编译器使用pack pragma 即时更改打包。所以你可以在定义之前用#pragma pack(push, 1) 和在定义之后用#pragma pack(pop) 包围struct recipe 的定义。这将使struct recipe 以 1 字节对齐方式打包,但其他所有内容都以默认对齐方式打包。见pack pragma
  • 您的文件是任何想要读取或写入文件的人的“界面”。它要求打包被明确定义,以便任何人都可以独立于编译器进行读写。
  • "recipe 是一个结构,我不会在此处包含它,因为它非常大。" 您至少应该提供一个省略的版本 - 甚至只是它的名称/标签- 如果使用您的命名,答案会更有意义。
  • 我在头文件中添加了#pragma pack(push, 1) 但我仍然没有得到正确的数据...

标签: c++ c visual-studio-2019 struct-member-alignment


【解决方案1】:

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

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

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

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

【讨论】:

  • 我尝试了各种形式,但都没有效果。唯一有效的是切换回 Struct Member Alignment: 1 Byte (/Zp1)
  • @Rugbrød 打包指令是一种快速而简单的解决方案。我还建议你反序列化。我猜这不是你尝试的。您最好了解为什么它不起作用,为此您应该使用调试器查看与读取相关的内存以及它为什么不适合结构。
猜你喜欢
  • 1970-01-01
  • 2016-08-24
  • 2019-05-21
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-29
  • 2012-12-08
  • 1970-01-01
相关资源
最近更新 更多