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

fwrite触发段错误排查:简易文件系统实现问题咨询

问题描述

我正在按照一份指南实现简易文件系统,采用文件读写接口。代码尚未重构,但执行super block的fwrite语句时触发段错误,注释该行后程序可正常运行,请指出可能的错误。

相关代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TOTAL_FILE_SIZE 4227104
#define TOTAL_DIR_ENTRIES 1024
#define NUM_FAT_ENTRIES 8192

typedef struct super_block {
    unsigned short total_sectors;           /* max value 8192 */
    unsigned short bytes_per_sector;        /* max value 512 */
    unsigned short available_sectors;       /* max value 8192 */
    unsigned short total_dir_entries;       /* fixing it to 1024 */
    unsigned short available_dir_entries;   /* max value 1024 */
    unsigned char  label[8];                /* label for the filesystem */

    /* These are not used */
    unsigned short sectors_per_cluster;     /* We always set it to 1*/
    unsigned char  reserved[12];            /* Just making it 32 bytes */
} super_block;

/* Each dir entry is 16 bytes */
typedef struct dir_entry {
    unsigned char  file_name[10];
    unsigned short fat_entry;
    unsigned int   size;
} dir_entry;

/* Each FAT entry is 2 bytes; times 8192 entries => 16 KiB
 * size of data block (sector) can be varied
 * number of dir_entries can be varied
 * the number of fat entries is same as the number of data blocks
 * the number of data blocks can be varied total_sectors
 *
 * the total size of our filesystem is => +
 * size of super block = 32 bytes
 * size of dir entry = 16 KiB
 * size of fat = 16 KiB
 * total size of data block = 8192 * 512 bytes => 4 MiB
 * Total bytes = 4227104
 * End: sFAT.c
*/

/* mkfs.c
 *
*/

/* Later: integrate label of FS to argument */
void mkfs(const char *filename) {
    /* Write super block, dir entries and fat table and data blocks to the file */
    super_block sb = {
            .total_sectors = 8192,
            .bytes_per_sector = 512,
            .available_sectors = 512,
            .total_dir_entries = 1024,
            .available_dir_entries = 1024,
            .sectors_per_cluster = 1,
            .label = "Demo FS",
            .reserved = ""
    };

    dir_entry default_dir_entry = {
            .file_name = "",
            .fat_entry = 0,
            .size = 0,
    };

    unsigned short default_fat_entry = 0;

    /* Write sb, 1024 dir_entry and 8192 default_fat_entry and 4 MiB data to create our filesystem */
    FILE *f = fopen(filename, "wb");
    if (f == NULL) {
        fprintf(stderr, "Error opening file: %s\n", filename);
    }

    /* Write super block */
    /* I seem to encounter SEGMENTATION FAULT in this line */
    fwrite(&sb, sizeof (super_block), 1, f);
    /* Write dir entries */
    fwrite(&default_dir_entry, sizeof(dir_entry), TOTAL_DIR_ENTRIES, f);
    fwrite(&default_fat_entry, sizeof(unsigned short), NUM_FAT_ENTRIES, f);
    char *data;

    data = malloc(TOTAL_FILE_SIZE - ftell(f));
    fwrite(data, TOTAL_FILE_SIZE - ftell(f), 1, f);
    free(data);
    fclose(f);
    fprintf(stdout, "Filesystem created successfully!\n");
}
/* End: mkfs.c */

/* main.c
 *
*/

void display_help(char *program_name) {
    fprintf(stdout, "Usage: %s [function name]\n", program_name);
    fprintf(stdout, "Functions:\n");
    fprintf(stdout,"  mkfs <filename>\n");
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        display_help(argv[0]);
        return 1;
    }
    if (strcmp(argv[1], "mkfs") == 0) {
        if (argc < 3) {
            fprintf(stderr, "mkfs requires a filename argument\n");
            display_help(argv[0]);
            return 1;
        }
        mkfs(argv[2]);
    }
    else {
        display_help(argv[0]);
        return 1;
    }
    return 0;
}

/*
 *
 * End: main.c
 */
错误分析与修复方案

1. 结构体字符数组初始化错误

C语言中不能直接用字符串常量给字符数组赋值,你在初始化super_block和dir_entry时犯了这个错误:

  • .label = "Demo FS":试图把字符串指针赋值给unsigned char[8]类型的数组,会破坏结构体内存布局,导致fwrite访问非法内存。
  • .reserved = "":reserved是unsigned char[12],同样不能直接用空字符串赋值。
  • .file_name = "":dir_entry的file_name是unsigned char[10],该赋值方式同样非法。

修复方法:
使用strcpy或memset初始化字符数组,或用初始化列表逐个赋值:

// 初始化super_block
super_block sb = {
        .total_sectors = 8192,
        .bytes_per_sector = 512,
        .available_sectors = 512,
        .total_dir_entries = 1024,
        .available_dir_entries = 1024,
        .sectors_per_cluster = 1
};
strcpy((char*)sb.label, "Demo FS"); // 强制类型转换适配unsigned char数组
memset(sb.reserved, 0, sizeof(sb.reserved)); // 清零reserved数组

// 初始化dir_entry
dir_entry default_dir_entry = {
        .fat_entry = 0,
        .size = 0
};
memset(default_dir_entry.file_name, 0, sizeof(default_dir_entry.file_name));

2. 文件打开失败后未终止函数

当fopen失败时,f的值为NULL,但你没有退出mkfs函数,后续fwrite(&sb, ..., f)会对NULL指针操作,直接触发段错误。

修复方法:
打开文件失败后立即返回,避免后续非法操作:

FILE *f = fopen(filename, "wb");
if (f == NULL) {
    fprintf(stderr, "Error opening file: %s\n", filename);
    return; // 终止函数,避免后续操作空指针
}

3. main函数参数检查缺失

执行mkfs时需要传入文件名(即argv[2]),但原代码未检查argc >= 3,若用户只输入./program mkfs,argv[2]会是NULL,传给mkfs后fopen(NULL)失败,进而触发段错误。

修复方法:
在main函数中添加参数检查:

if (strcmp(argv[1], "mkfs") == 0) {
    if (argc < 3) {
        fprintf(stderr, "Error: mkfs requires a filename argument\n");
        display_help(argv[0]);
        return 1;
    }
    mkfs(argv[2]);
}

4. 额外优化:malloc内存未初始化

malloc分配的内存是未初始化的垃圾数据,虽不影响段错误,但如果需要数据块全为0,建议用calloc或memset清零:

data = calloc(TOTAL_FILE_SIZE - ftell(f), 1); // 分配并自动清零内存
// 或
data = malloc(TOTAL_FILE_SIZE - ftell(f));
if (data != NULL) {
    memset(data, 0, TOTAL_FILE_SIZE - ftell(f));
}

内容的提问来源于stack exchange,提问作者Udeshya D.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 02:25:02