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

时间:2019-03-27 13:54:43

标签: c

我编写了一个函数,该函数读取二进制文件中的struct中的double值,然后计算该值,然后将计算出的double值写入二进制文件中。

结构为:

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(函数oldCharge中的update())的先前值。费用的初始值为20,我在update函数中输入40。成功写入后,我读取了charge的值,发现它既不是20也不是40,而是一个奇怪的32.55(我保留了两位小数)。

我尝试修改logDate,并且logNote可以被成功修改(当然offset是不同的)。只有双值chargetotal会有奇怪的结果。

原因是什么?

4 个答案:

答案 0 :(得分:4)

您阅读了完整的 logData ,可以修改read元素,然后将其全部写入,可能是您有填充问题,因此计算出的偏移量是错误的

chrome.runtime.onMessage.addListener(
(request, sender, sendResponse) => {
    chrome.tabs.create({url: "example.com"}, tab => {
        chrome.tabs.executeScript(tab.id, {code: var x = 10; x}, function (result) {
            sendResponse(result);    
        });
    });
});

否则使用 offsetof 仅写入 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 */
}

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

答案 1 :(得分:4)

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

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

size_t offset = offsetof(struct logData, charge);

答案 2 :(得分:1)

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

#include<stddef.h>

long offset = offsetof(struct logData, charge);    

答案 3 :(得分:1)

  

我编写了一个函数,该函数读取二进制文件中struct中的double值,然后计算该值,然后将计算出的double值写入二进制文件中。

请注意,二进制文件本来就不可移植。如果要处理由同一C实现构建的另一个C程序在同一台计算机上创建的文件,那么您应该可以,但否则,编写者和阅读者都需要非常注意二进制兼容性问题

但是,即使在那种情况下,您的代码也做出了不安全的假设。结构构件(第一个构件除外)的偏移量不能保证与先前构件的大小之和相同。结构可能并且经常确实在成员之间以及最后一个成员之后包含填充,这将使您的计算陷入困境。

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

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