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

如何将结构体数组成员作为fread()的第一个参数?报错排查

BMP图像读写程序错误修复

问题描述

在readPixelsBMP函数中执行:

fread((*pArr)[i][j]->b, sizeof(unsigned char), 1, file);

收到警告:

passing argument 1 of 'fread' makes pointer from integer without a cast

改为:

fread(&(*pArr)[i][j]->b, sizeof(unsigned char), 1, file);

无警告但程序崩溃,程序用于读取并写入BMP图像。

错误原因分析

  1. 指针访问语法错误:(*pArr)[i][j]是struct Pixel类型的直接变量,不是指针,因此访问成员应该用.运算符,而非->。使用->会将变量值当作指针地址,导致非法内存访问,引发崩溃。
  2. 循环顺序错误:BMP图像的像素数据是从左下角开始,逐行向上存储的,原代码外层循环遍历width、内层遍历height,会导致像素读取顺序完全错误,同时越界访问内存。
  3. 像素读取顺序错误:BMP中单个像素以B-G-R的字节顺序存储,原代码只读取了b分量,且未按正确顺序读取g、r分量。
  4. 文件关闭时机不当:readPixelsBMP函数内关闭了输入文件,但文件是在main中打开的,应由打开者负责关闭,避免后续代码误用已关闭的文件指针。
  5. 写入函数逻辑错误:writePixelsBMP中每次写入整个像素数组而非单个像素,且填充padding时写入字符串"0"(ASCII值48),而非BMP要求的0值字节。

修复后的完整代码

#include <stdio.h>
#include <stdlib.h>

struct BMP_Header {
    char signature[2];
    int size;
    short reserved1;
    short reserved2;
    int offset_pixel_array;
};

struct DIB_Header{
    int  size;
    int width;
    int height;
    short planes;
    short bits_per_pixel;
    int compression;
    int image_size;
    int x_per_meter;
    int y_per_meter;
    int colors_in_table;
    int important_colors;
};

struct Pixel{
    unsigned char b;  // 对应BMP的蓝色分量,调整顺序方便直接读取
    unsigned char g;  // 绿色分量
    unsigned char r;  // 红色分量
};

void readBMPHeader(FILE* file, struct BMP_Header* header) {
    fread(header->signature, sizeof(char)*2, 1, file);
    fread(&header->size, sizeof(int), 1, file);
    fread(&header->reserved1, sizeof(short), 1, file);
    fread(&header->reserved2, sizeof(short), 1, file);
    fread(&header->offset_pixel_array, sizeof(int), 1, file);
}

void readDIBHeader(FILE* file, struct DIB_Header* header) {
    fread(&header->size, sizeof(int), 1, file);
    fread(&header->width, sizeof(int), 1, file);
    fread(&header->height, sizeof(int), 1, file);
    fread(&header->planes, sizeof(short), 1, file);
    fread(&header->bits_per_pixel, sizeof(short), 1, file);
    fread(&header->compression, sizeof(int), 1, file);
    fread(&header->image_size, sizeof(int), 1, file);
    fread(&header->x_per_meter, sizeof(int), 1, file);
    fread(&header->y_per_meter, sizeof(int), 1, file);
    fread(&header->colors_in_table, sizeof(int), 1, file);
    fread(&header->important_colors, sizeof(int), 1, file);
}

void readPixelsBMP(FILE* file, int width, int height, struct Pixel (**pArr)[width][height]) {
    *pArr = malloc(sizeof(struct Pixel[width][height]));
    if (*pArr == NULL) {
        printf("Error! Insufficient memory!");
        return;
    }

    int padding = (4 - ((3*width) % 4)) % 4;
    // BMP像素从左下角开始存储,所以从最后一行开始读取
    for (int i = height - 1; i >= 0; i--) {
        for (int j = 0; j < width; j++) {
            // 直接读取整个Pixel结构体,对应B-G-R顺序
            fread(&(*pArr)[i][j], sizeof(struct Pixel), 1, file);
        }
        if (padding > 0) {
            fseek(file, padding, SEEK_CUR);
        }
    }
}

void writeBMPHeader(FILE* file, struct BMP_Header* header) {
    fwrite(header->signature, sizeof(char)*2, 1, file);
    fwrite(&header->size, sizeof(int), 1, file);
    fwrite(&header->reserved1, sizeof(short), 1, file);
    fwrite(&header->reserved2, sizeof(short), 1, file);
    fwrite(&header->offset_pixel_array, sizeof(int), 1, file);
}

void writeDIBHeader(FILE* file, struct DIB_Header* header) {
    fwrite(&header->size, sizeof(int), 1, file);
    fwrite(&header->width, sizeof(int), 1, file);
    fwrite(&header->height, sizeof(int), 1, file);
    fwrite(&header->planes, sizeof(short), 1, file);
    fwrite(&header->bits_per_pixel, sizeof(short), 1, file);
    fwrite(&header->compression, sizeof(int), 1, file);
    fwrite(&header->image_size, sizeof(int), 1, file);
    fwrite(&header->x_per_meter, sizeof(int), 1, file);
    fwrite(&header->y_per_meter, sizeof(int), 1, file);
    fwrite(&header->colors_in_table, sizeof(int), 1, file);
    fwrite(&header->important_colors, sizeof(int), 1, file);
}

void writePixelsBMP(FILE* file, int width, int height, struct Pixel (*pArr)[width][height]) {
    int padding = (4 - ((3*width) % 4)) % 4;
    // 按BMP存储顺序,从最后一行写到第一行
    for (int i = height - 1; i >= 0; i--) {
        for (int j = 0; j < width; j++) {
            fwrite(&(*pArr)[i][j], sizeof(struct Pixel), 1, file);
        }
        if (padding > 0) {
            // 写入padding的0值字节
            unsigned char pad[3] = {0};
            fwrite(pad, sizeof(unsigned char), padding, file);
        }
    }
}

int main() {
    char* filename = "test1wonderbread.bmp";
    char* output = "test3wonderbread.bmp";
    FILE* file_input = fopen(filename, "rb");
    if (!file_input) {
        printf("Failed to open input file!\n");
        return 1;
    }
    FILE* file_output = fopen(output, "wb");
    if (!file_output) {
        printf("Failed to open output file!\n");
        fclose(file_input);
        return 1;
    }

    struct BMP_Header* header = malloc(sizeof(struct BMP_Header));
    readBMPHeader(file_input, header);
    struct DIB_Header* dib_header = malloc(sizeof(struct DIB_Header));
    readDIBHeader(file_input, dib_header);

    // 检查是否为24位无压缩BMP(当前代码仅支持该格式)
    if (dib_header->bits_per_pixel != 24 || dib_header->compression != 0) {
        printf("Only 24-bit uncompressed BMP is supported!\n");
        fclose(file_input);
        fclose(file_output);
        free(header);
        free(dib_header);
        return 1;
    }

    struct Pixel (*pArr)[dib_header->width][dib_header->height];
    readPixelsBMP(file_input, dib_header->width, dib_header->height, &pArr);

    writeBMPHeader(file_output, header);
    writeDIBHeader(file_output, dib_header);
    writePixelsBMP(file_output, dib_header->width, dib_header->height, pArr);

    // 释放资源
    fclose(file_input);
    fclose(file_output);
    free(header);
    free(dib_header);
    free(pArr);

    printf("BMP processed successfully!\n");
    return 0;
}

关键修复点说明

  • 调整struct Pixel成员顺序为b-g-r,直接匹配BMP的存储顺序,可一次性读取整个结构体,简化代码。
  • 修正指针访问语法:将(*pArr)[i][j]->b改为(*pArr)[i][j].b(或直接读取整个结构体)。
  • 修正循环顺序:从height-1到0遍历行,匹配BMP的像素存储方向。
  • 移动文件关闭操作到main函数,由打开者负责关闭文件。
  • 修复writePixelsBMP的写入逻辑:写入单个像素,并用0值字节填充padding。
  • 添加文件打开失败、BMP格式不支持的错误处理,提升程序健壮性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 22:15:55