为什么用C修改二进制文件的双精度是错误的?



我编写了一个函数,该函数读取二进制文件中结构中的双精度值,并计算该值,然后将计算出的双精度值写入二进制文件。

结构为:

struct logData{
long logId; 
char logDate[11]; 
char logNote[20];  
double charge;   
double total;    
};

读取和修改数据charge的函数为:

long size = sizeof(struct logData);
void update(FILE* fp, int n){
fseek(fp,(n-1)*size, SEEK_SET);
struct logData thisLogData;
fread(&thisLogData,size,1,fp);
long offset = sizeof(thisLogData.logId)+sizeof(thisLogData.logNote)+ sizeof(thisLogData.logDate);
double oldCharge = thisLogData.charge;
scanf("%lf", &thisLogData.charge);
fseek(fp, -size+offset, SEEK_CUR);
fwrite(&thisLogData.charge, sizeof(thisLogData.charge), 1, fp);
fclose(fp);
}

许多结构:logData存储在二进制文件中。参数:n表示logData的位置。

我成功地读出了charge的先前值(函数update()中的oldCharge)。电荷的初始值是20,我在update函数中输入40。写成功后,我读了charge的值,发现它既不是20也不是40,而是一个奇怪的32.55(我保留了两位小数)。

我尝试修改logDatelogNote可以成功修改(当然offset不同)。只有双精度值chargetotal才会产生奇怪的结果。

原因是什么?

long offset = sizeof(thisLogData.logId)+sizeof(thisLogData.logNote)+ sizeof(thisLogData.logDate);

这不是成员的偏移量。结构成员具有填充。使用offsetof.

size_t offset = offsetof(struct logData, charge);

你读取一个完整的日志数据,你可以修改读取元素然后写入所有元素,可能是你有填充问题,所以计算的偏移量是错误的

bool update(FILE* fp, int n){
struct logData thisLogData;
if ((fseek(fp,(n-1)*size, SEEK_SET) == -1) ||
(fread(&thisLogData,size,1,fp) == 1)) {
??? indicate error ???
fclose(fp);
return false;
}
if (scanf("%lf", &thisLogData.charge) != 1) {
??? indicate error ???
fclose(fp);
return false;
}
fseek(fp,(n-1)*size, SEEK_SET);
return ((fwrite(&thisLogData,size,1,fp) != -1)
& (fclose(fp) != -1); /* not && because have to close in all cases */
}

否则使用offsetof只写入新的 charge值而不读取元素:

bool update(FILE* fp, int n){
double d;
if (scanf("%lf", &d) != 1) {
??? indicate error ???
fclose(fp);
return false;
}
if (fseek(fp,(n-1)*size + offsetof(struct logData, charge), SEEK_SET) == -1) {
??? indicate wrong n / file ???
fclose(fp);
return false;
}
return ((fwrite(&d,sizeof(double),1,fp) != -1) 
& (fclose(fp) != -1)); /* not && because have to close in all cases */
}

注意:奇怪的是,该功能必须关闭文件但不打开它,不在此处关闭更合乎逻辑

我编写了一个函数,该函数读取二进制文件中结构中的双精度值,并计算该值,然后将计算出的双精度值写入二进制文件。

请注意,二进制文件本质上是不可移植的。 如果您正在操作由同一 C 实现构建的另一个 C 程序在同一台计算机上创建的文件,那么您应该没问题,但除此之外,编写器和读取器都需要特别注意二进制兼容性问题。

但是,即使在这种情况下,您的代码也会做出不安全的假设。 结构成员(第一个成员除外)的偏移量不保证与前面成员的大小之和相同。 结构可能并且经常包含成员之间和最后一个成员之后的填充,这会破坏您的计算。

尽管可以计算结构成员的偏移量,包括没有offsetof()的旧 C 版本中,但在这种情况下,我建议通过将整个结构写回文件而不是仅将单个成员来完全回避这个问题:

fseek(fp, -size, SEEK_CUR);
fwrite(&thisLogData, size, 1, fp);

您应该使用offsetof来找出结构中字段的偏移量:

#include<stddef.h>
long offset = offsetof(struct logData, charge);    

相关内容

  • 没有找到相关文章