问题描述
我有一个较旧的项目,它使用 fread() 将二进制文件读入结构体。 它使用 Visual Studio 2017 (v141)
我将项目升级到最新的C++工具版本(v142),用于VS 2019,并将解决方案从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,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 指令添加到结构中,如下所示:
#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;
但它似乎对错误没有任何影响。
解决方法
/Zp1
总是一个坏主意 - 它在全局范围内应用包装。通过更改包装,您会使现有文件不兼容。相反,您应该有选择地打包:
#pragma pack(1) // 1 byte packing
struct sRecipe
{
...
} ;
#pragma pack() // restore default packing
然而,这可能无法解决编译器、编译器版本和目标之间的所有兼容性问题(并且您还将目标从 AnyCPU 更改为 x86)。实际上,最好使用 CSV、XML 或具有“网络字节顺序”的二进制文件(使用 htonl()
/nltoh()
et al)序列化/反序列化保存到文件中的数据例如,为了避免原始结构的对齐和字节顺序问题。
本质上,您应该编写代码来显式生成您为文件指定的格式,二进制文件逐字节生成,否则使用明确的字符串表示。