如何遍历结构体数组并实现读写?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; }
关键修改说明
- 修正Pixel结构体:调整成员顺序为
b、g、r,匹配BMP存储格式,避免颜色反转 - 调整循环逻辑:按BMP倒序行存储规则,从最后一行开始遍历,先处理行再处理列
- 修正读写地址:用
&(*pArr)[j][i]定位每个像素的地址,确保读写不同位置 - 修复文件管理:移除
readPixelsBMP中的fclose,在main中统一关闭文件 - 修正padding写入:使用二进制0数组填充,符合BMP格式要求
- 添加错误检查:增加文件打开失败的判断,避免空指针操作
- 内存释放:添加malloc分配内存的释放操作,避免内存泄漏
内容的提问来源于stack exchange,提问作者Ashton S
相关产品推荐
相关产品推荐

