You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从结构体数组删除元素并使用fwrite正确覆盖文件

解决C语言图书馆程序删除书籍后文件覆盖的问题

嘿,我来帮你搞定这个问题!你遇到的核心问题其实有两个:一是内存数组操作的小bug,二是**"r+b"模式下文件不会自动缩短,残留的旧数据导致读取异常**。咱们一步步来解决:

首先,揪出代码里的关键问题

1. 操作数组时的越界错误

你的remove_book函数里,前移元素的循环条件有问题:

for (int index = i ; index < *num ; index++) 
    bibl[index] = bibl[index + 1];

当index等于*num - 1时,bibl[index+1]会访问数组的越界位置(比如数组最大索引是MAXBOOK-1,你却访问了MAXBOOK),这会导致未定义行为,甚至把随机数据塞进数组里。

2. 每次操作都重新读取文件,覆盖内存修改

在case 2(删除书籍)里,你先调用了input(bibl, &counter, &counterp),这会把文件里的旧数据重新读入数组,直接覆盖你刚在内存中删除元素后的数组——等于白删了!

3. 文件未截断,残留旧数据

用r+b模式打开文件时,如果你只写入前counter个有效结构体,文件后面的旧数据不会自动消失。比如原来文件有3个结构体,删除后写入2个,文件长度还是3个结构体的大小,读取时会把最后那个旧数据也读进来。


具体修复步骤和代码修改

步骤1:修正remove_book函数的数组操作

修改前移循环的条件,避免越界,同时增加书籍未找到的判断:

void remove_book(info bibl[], int * num) {
    char title[256];
    puts("Enter the title of the book you want to remove:");
    read(title, 256);

    // 先找到要删除的书籍索引
    int found_idx = -1;
    for (int i = 0 ; i < *num ; i++) {
        if (strcmp(title, bibl[i].title) == 0) {
            found_idx = i;
            break;
        }
    }

    if (found_idx == -1) {
        puts("Oops, that book isn't in the library!");
        return;
    }

    // 前移元素,循环到*num-2即可,避免访问越界
    for (int index = found_idx ; index < *num - 1 ; index++) {
        bibl[index] = bibl[index + 1];
    }

    // 有效书籍数量减1,不需要赋值空结构体(后面不会写入这个位置)
    (*num)--;

    // 打印当前库中的书籍
    puts("Updated library contents:");
    for (int index = 0 ; index < *num ; index++) {
        printf("%s, author: %s, price: %.2f €\n", 
               bibl[index].title, bibl[index].autor, bibl[index].price);
    }
}

步骤2:修正菜单中的操作逻辑,避免重复读取文件

在main函数的菜单循环里,不要每次操作都调用input——内存里的数组已经是最新状态了,直接修改数组后写入文件即可。以case 2为例:

case 2 : 
    remove_book(bibl, &counter);
    // 将文件指针移到开头,准备覆盖写入
    rewind(fp); 
    // 写入前counter个有效结构体
    size_t written = fwrite(&bibl[0], sizeof(info), counter, fp);
    if (written != counter) {
        fprintf(stderr, "Error writing to file! Some data might be lost.\n");
    }
    // 关键:截断文件,去掉后面的旧数据
    #ifdef _WIN32
        // Windows系统用_chsize
        if (_chsize(fileno(fp), counter * sizeof(info)) == -1) {
            fprintf(stderr, "Error truncating file on Windows.\n");
        }
    #else
        // POSIX系统(Linux/macOS)用ftruncate
        if (ftruncate(fileno(fp), counter * sizeof(info)) == -1) {
            fprintf(stderr, "Error truncating file on POSIX system.\n");
        }
    #endif
    break;

步骤3:确保初始读取逻辑正确

你的input函数应该正确读取文件中所有结构体到数组,初始化counter为当前书籍数量:

void input(info bibl[], int *counter, int *counterp) {
    *counterp = 0;
    // 逐个读取结构体,直到文件结束
    while (fread(&bibl[*counterp], sizeof(info), 1, fp) == 1) {
        (*counterp)++;
    }
    *counter = *counterp; // 初始时有效书籍数量等于读取到的数量
}

原理说明

  1. 数组操作修正:前移元素时只循环到*num-2,保证不会访问数组越界的位置,避免引入随机数据。
  2. 避免重复读取文件:内存数组是唯一的数据源,所有增删改操作都直接修改内存数组,操作完成后再写入文件,不会覆盖修改结果。
  3. 文件截断:ftruncate(POSIX)或_chsize(Windows)会把文件长度调整为counter * sizeof(info),这样文件里就只会保留有效数据,后续读取时不会读到旧的残留数据。

这样修改后,你删除书籍再写入文件,读取时就只会得到有效结构体了!

内容的提问来源于stack exchange,提问作者n01s0n

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:51:06