如何将结构体数组成员作为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图像。
错误原因分析
- 指针访问语法错误:
(*pArr)[i][j]是struct Pixel类型的直接变量,不是指针,因此访问成员应该用.运算符,而非->。使用->会将变量值当作指针地址,导致非法内存访问,引发崩溃。 - 循环顺序错误:BMP图像的像素数据是从左下角开始,逐行向上存储的,原代码外层循环遍历width、内层遍历height,会导致像素读取顺序完全错误,同时越界访问内存。
- 像素读取顺序错误:BMP中单个像素以B-G-R的字节顺序存储,原代码只读取了b分量,且未按正确顺序读取g、r分量。
- 文件关闭时机不当:
readPixelsBMP函数内关闭了输入文件,但文件是在main中打开的,应由打开者负责关闭,避免后续代码误用已关闭的文件指针。 - 写入函数逻辑错误:
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
相关产品推荐
相关产品推荐

