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

如何遍历结构体数组并实现读写?BMP像素处理代码报错求助

BMP像素读写函数错误:重复输出首个像素的问题

我尝试用fread((*pArr)[i][j], sizeof(struct Pixel), 1, file);,也试过指针算术,但始终没法正确实现writePixelsBMP和readPixelsBMP函数,现在只能错误地重复输出首个像素。以下是我的完整代码:

#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 r;
    unsigned char b;
    unsigned char g;
};

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;
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            fread(*pArr, sizeof(struct Pixel), 1, file);
        }
        if (padding) {
            fseek(file, padding, SEEK_CUR);
        }
    }
    fclose(file);
}

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;
    for (int i = 0; i < width; i++) {
        for (int j = 0; j < height; j++) {
            fwrite(*pArr, sizeof(struct Pixel), 1, file);
        }
        if (padding)
            fwrite("0", sizeof(char), padding, file);
    }
    char eof = 0x00;
    fwrite(&eof, sizeof(char), 1, file);
}

int main() {
    char* filename = "test1wonderbread.bmp";
    char* output = "test2wonderbread.bmp";
    FILE* file_input = fopen(filename, "rb");
    FILE* file_output = fopen(output, "wb");

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

    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);
    printf("no errors");
    return 0;
}

问题分析与修复

核心问题

  • 读写地址错误:fread(*pArr, ...)和fwrite(*pArr, ...)始终指向数组起始地址,每次都覆盖/写入首个像素,未遍历后续位置
  • 循环顺序错误:BMP像素按行从下到上存储,且应先遍历高度(行)再遍历宽度(列),原代码循环顺序颠倒
  • 像素字节顺序错误:BMP存储像素为蓝(B)、绿(G)、红(R),原Pixel结构体顺序为r、b、g,导致颜色反转
  • 错误关闭文件:readPixelsBMP中提前关闭文件,后续操作会出错
  • padding写入错误:用ASCII字符'0'填充,而非BMP要求的二进制0x00

修复后的代码

#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;
};

// 修正BMP像素字节顺序:B、G、R
struct Pixel{
    unsigned char b;
    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像素从最后一行开始存储,先遍历行(height)再遍历列(width)
    for (int i = height - 1; i >= 0; i--) {
        for (int j = 0; j < width; j++) {
            // 定位到当前像素地址
            fread(&(*pArr)[j][i], 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)[j][i], sizeof(struct Pixel), 1, file);
        }
        if (padding > 0) {
            // 写入二进制0填充
            const char pad[3] = {0};
            fwrite(pad, sizeof(char), padding, file);
        }
    }
}

int main() {
    char* filename = "test1wonderbread.bmp";
    char* output = "test2wonderbread.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);

    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);

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

    printf("Operation completed successfully!\n");
    return 0;
}

关键修改说明

  1. 修正Pixel结构体:调整成员顺序为b、g、r,匹配BMP存储格式,避免颜色反转
  2. 调整循环逻辑:按BMP倒序行存储规则,从最后一行开始遍历,先处理行再处理列
  3. 修正读写地址:用&(*pArr)[j][i]定位每个像素的地址,确保读写不同位置
  4. 修复文件管理:移除readPixelsBMP中的fclose,在main中统一关闭文件
  5. 修正padding写入:使用二进制0数组填充,符合BMP格式要求
  6. 添加错误检查:增加文件打开失败的判断,避免空指针操作
  7. 内存释放:添加malloc分配内存的释放操作,避免内存泄漏

内容的提问来源于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:07:32