【问题标题】:Different behaviour of fwrite between Linux and WindowsLinux 和 Windows 之间 fwrite 的不同行为
【发布时间】:2026-01-16 16:20:07
【问题描述】:

我有一个用 C 编写的小示例程序。我有一个调用函数 writeFile 的主程序,该函数将一些数字写入二进制文件。然后我调用overwrite 将 0 替换为 1,最后打印结果。

这是代码:

#include <stdio.h>

/* Print the content of the file */
void printFile(){
    printf("Read test.dat:\n");
    int r;
    FILE* fp = fopen("test.dat", "rb+");
    if(fp) {
    while(fread(&r,sizeof(int),1,fp)){
            printf("%d\n", r);
        }
    }
    fclose(fp);
}

/* Replace 0 with 1 */
void overwrite(){
    int   r;
    FILE *fp = fopen("test.dat", "rb+");
    if (fp) {
        int i=0;
        while (i < 4 && fread(&r, sizeof(int), 1, fp)) {
            i++;
            if (r == 0) {
                r = 1;
                fseek(fp,-sizeof(int),SEEK_CUR);
                fwrite(&r,sizeof(int),1,fp);
            }
        }
    }
    fclose(fp);    
}

/* Create original file */
void writeFile() {
    int b, b1, b2, b3, b4;

    b  = 3;
    b1 = 2;
    b2 = 0;
    b3 = 4;

    FILE *fp = fopen("test.dat", "wb");
    if (fp) {
        fwrite(&b, sizeof(int), 1, fp);
        fwrite(&b1, sizeof(int), 1, fp);
        fwrite(&b2, sizeof(int), 1, fp);
        fwrite(&b3, sizeof(int), 1, fp);
    }
    fclose(fp);    
}

int main() {
    writeFile();
    printf("---------BEFORE--------\n");
    printFile();
    printf("-----------------------\n");
    printf("Overwriting...\n");
    overwrite();
    printf("---------AFTER---------\n");
    printFile();
    return 0;
}

这段代码适用于 Linux,但是当我在 Windows 上运行相同的代码时,输​​出是这样的:

 ---------BEFORE--------
Read test.dat:
3
2
0
4
-----------------------
Overwriting...
---------AFTER---------
Read test.dat:
3
2
1
2

不仅0被1替换,而且最后一个数字也被改变了。有人可以帮助我理解为什么会发生这种情况?

另一个问题是在overwrite 中我必须使用i 来停止while,因为没有i&lt;4 我会得到一个无限循环(仅限Windows)。

我在使用 gcc 4.8.1(来自 MinGW)编译的 Windows 8.1 上测试了此代码。 在我的 Linux 机器上,我使用 gcc 5.1.1 测试了代码。

谢谢大家,

【问题讨论】:

  • 如果您将0 之前的2 更改为5,它会用5 替换最后一个数字吗?
  • 我无法复制它。我有预期的输出序列 3、2、1、4。
  • 我很想知道我在 VBox 机器上安装 windows 是否可重现,现在我正在安装 DevCPP 进行测试。
  • 我刚刚做到了,它确实发生在 Windows 上。它确实用第二个数字替换了最后一个数字。

标签: c linux windows fwrite


【解决方案1】:

这是因为你需要在fwrite() 之后再调用fflush(),因为你不应该在调用fwrite() 之后再调用fread(),而中间没有调用fflush()

这是与这种情况相关的标准部分

7.21.5.3 fopen 函数

  1. 当以更新模式('+' 作为上述模式参数值列表中的第二个或第三个字符)打开文件时,可以在关联的流上执行输入和输出。但是,输出不得直接跟在输入后面没有 干预对fflush 函数或文件定位函数(fseekfsetposrewind)的调用,并且输入不应直接跟在没有 干预对文件定位函数的调用,除非输入操作遇到文件结尾。在某些实现中,使用更新模式打开(或创建)文本文件可能会打开(或创建)二进制流。

这部分在fopen() 函数中很奇怪,因为它涉及fread()fwrite(),这是我正在寻找答案的地方。

您还可以看到我的previous answer 工作但不是因为我在其中说明的原因,而是在上面的段落中找到了解释。

【讨论】:

  • fclose(fp) 应该在fp != NULL 分支中(这也适用于问题中的原始代码)。
  • 我之前的回答是错误的对不起,我必须承认它没有多大意义。
  • 谢谢,现在可以使用了!!在您修复之前,我尝试放一些 ftell(fp) 来查看指针位置,一切似乎都很好。每次读取后指针增加,当我替换它返回的值时(在 fseek 之后),然后向前返回(在 fwrite 之后)。为什么指针似乎在正确的位置?
  • 这是正确的行为,问题与此无关,正如您从我的最终答案中看到的那样。找不到答案真的让我很困扰。
【解决方案2】:

Stream I/O 的 MSDN 状态:

只有通过对fflush 或文件定位函数(fseekfsetposrewind)的介入调用,输入才能直接跟随输出。如果输入操作遇到文件末尾,则输出可以跟随输入而无需对文件定位函数进行干预调用。

所以你必须在调用 fwrite 后调用fflush()

void overwrite()
{
    FILE *fp = fopen("test.dat", "rb+");
    if (fp) {
        int   r;
        while (fread(&r, sizeof(int), 1, fp) == 1) {
            if (r == 0) {
                r = 1;
                fseek(fp,-sizeof(int),SEEK_CUR);
                fwrite(&r,sizeof(int),1,fp);
                fflush(fp); /* Flush here */
            }
        }
        fclose(fp); /* Also: call fclose() only when fopen() succeeded */
    }

}

【讨论】:

  • 好吧,我确实引用了文本并指出:“输入可以直接跟随输出,只有在中间调用 fflush 或 [...]”。我不明白这有什么问题。
  • 哦 - 我没有注意到你也在编辑我的帖子。但我仍然看不出我的答案有什么问题:|
  • 我明白了,它与标准文档的顺序相反,它说 only with ... 而标准说 没有介入调用 ... ... 很抱歉,请试着理解,虽然我知道我的英语很好,但这不是我的母语。
  • 好的 - 不用担心,感谢更好的格式:)
  • MinGW 链接到 MSVCRT:mingw.org/wiki/c99,因此 MSDN 也应该适用于这种情况。不过,引用 C 标准绝对是更好的来源。
最近更新 更多