C语言 如何使用结构体数组的成员作为fread()的第一个参数?

sdnqo3pr  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(82)

我得到
传递“fread”的参数1使指针从整数变为不带强制转换的整数
当我做

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

在我的readPixelsBMP函数中。和

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

使其崩溃但不发出警告。我的程序只是一个程序,它应该读取一个图像并写入同一个图像。下面是我的全部代码:

#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);
            fread(&(*pArr)[i][j]->b, sizeof(unsigned char), 1, file);
            //fread((*pArr)[i][j]->r, sizeof(unsigned char), 1, file);
            //fread((*pArr)[i][j]->g, sizeof(unsigned char), 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 = "test3wonderbread.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;
}
mlnl4t2r

mlnl4t2r1#

发布的代码非常非常复杂,可能没有任何原因。
5 + 11次调用fread来获取位图头?5 + 11次调用fwrite来写相同的头?为什么会这样呢?

所有这些都是54字节?.

不算读取实际像素的调用...
我将在这里展示一种读取和写入位图的方法,包括代码和一些参数。这里的基本区别是位图的封装

**我将再次留下一篇长文章,因为我认为在这里有一个完整的代码来读取,绘制和编写C位图是有用的。

完全可以无视。

一个位图struct和几种方法

typedef struct
{
    char    header[2];
    int32_t fileSize;
    int32_t reserved;
    int32_t dataOffset;
} BitmapFileHeader;

typedef struct
{
    int32_t headerSize;
    int32_t width;
    int32_t height;
    int16_t planes;
    int16_t bitsPerPixel;
    int32_t compression;
    int32_t dataSize;
    int32_t horizontalResolution;
    int32_t verticalResolution;
    int32_t colors;
    int32_t importantColors;
} BitmapInfoHeader;

typedef struct
{
    BitmapFileHeader h_bmp;
    BitmapInfoHeader h_info;
    uint8_t*         pixel;
} Bitmap;

    Bitmap* bmp_create(unsigned w, unsigned h);
    Bitmap* bmp_destroy(Bitmap* bmp);
    int     bmp_fill(Bitmap*, uint8_t, uint8_t, uint8_t);
    int     bmp_grad(Bitmap* bmp);
    Bitmap* bmp_read(const char* filename);
    int     bmp_setPixel(
            Bitmap* bmp, int x, int y, uint8_t red, uint8_t green,
            uint8_t blue);
    int bmp_write(Bitmap* bmp, const char* filename);

bmp_grad在任何位图绘制区域上构建类似

的东西。考虑到它们的名字,其他函数做的是预期的事情。一个简单的(完整的)实现在文章的结尾。代码的测试和输出如下。

main()进行测试

下面的代码显示了正在使用的函数。该方案:

  • 创建1024x768 bmp
  • 用红色、绿色和蓝色填充位图,并将3个位图保存到磁盘。这是第一个的代码,red.bmp
res = bmp_fill(a_test, 255, 0, 0);  // red
    fprintf(stderr, "\tbmp_fill returned %d\n", res);
    res = bmp_write(a_test, "red.bmp");
    fprintf(stderr, "\tbmp_write() returned %d\n", res);
  • 使用bmp_read从磁盘读取其中一个位图,然后将其保存为blueCopy.bmp中的副本
Bitmap* a_copy = bmp_read("blue.bmp");
    fprintf(stderr, "\tbmp_read() read a [%d x %d], size is %d\n", 
        a_copy->h_info.width,
        a_copy->h_info.height,
        a_copy->h_bmp.fileSize);
    res = bmp_write(a_copy, "blueCopy.bmp");
    fprintf(stderr, "\tbmp_write() returned %d\n", res);
  • 这个的输出:
bmp_read() read a [1024 x 768], size is 3145782
        bmp_write() returned 0
  • 调用bmp_grad绘制条纹并将新位图保存为stripes.bmp
  • bmp_setPixel用于在stripes.bmp的中心绘制一条11像素高的黑色线条,然后将位图保存为lineset_Pixel.bmp,如下所示

main.c代码

#include "bitmap.h"
int main(void)
{ 
    Bitmap* a_test = bmp_create(1024, 768);
    if (a_test == NULL)
    {
        fprintf(stderr, "\tbitmap not created\n");
        return -1;
    }
    fprintf(stderr, "\tbitmap created ok!\n");
    int res = 0;

    res = bmp_fill(a_test, 255, 0, 0);  // red
    fprintf(stderr, "\tbmp_fill returned %d\n", res);
    res = bmp_write(a_test, "red.bmp");
    fprintf(stderr, "\tbmp_write() returned %d\n", res);

    res = bmp_fill(a_test, 0, 255, 0);  // green
    fprintf(stderr, "\tbmp_fill() returned %d\n", res);
    res = bmp_write(a_test, "green.bmp");
    fprintf(stderr, "\tbmp_write() returned %d\n", res);

    res = bmp_fill(a_test, 0, 0, 255);  // blue
    fprintf(stderr, "\tbmp_fill() returned %d\n", res);
    res = bmp_write(a_test, "blue.bmp");
    fprintf(stderr, "\tbmp_write() returned %d\n", res);

    Bitmap* a_copy = bmp_read("blue.bmp");
    fprintf(stderr, "\tbmp_read() read a [%d x %d], size is %d\n", 
        a_copy->h_info.width,
        a_copy->h_info.height,
        a_copy->h_bmp.fileSize);
    res = bmp_write(a_copy, "blueCopy.bmp");
    fprintf(stderr, "\tbmp_write() returned %d\n", res);
    
    res = bmp_grad(a_copy);
    fprintf(stderr, "\tbmp_grad() returned %d\n", res);
    res = bmp_write(a_copy, "stripes.bmp");
    fprintf(stderr, "\tbmp_write() returned %d\n", res);

    // draw a black line at the center of the bitmap
    for (int i = 0; i < a_copy->h_info.width; i += 1)
        for (int j = (a_copy->h_info.height / 2) - 5;
                 j < (a_copy->h_info.height / 2) + 5; j+=1)
        bmp_setPixel(a_copy, i, j, 0, 0, 0);
    res = bmp_write(a_copy, "lineset_Pixel.bmp");
    fprintf(stderr, "\tbmp_write() returned %d\n", res);

    a_test = bmp_destroy(a_test);
    a_copy = bmp_destroy(a_copy);
    fprintf(stderr, "\t[end of tests]\n");
    return 0;
}

Bitmap.h

#pragma once
#pragma pack(push, 2)
#pragma pack(show)

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

typedef struct
{
    char    header[2];
    int32_t fileSize;
    int32_t reserved;
    int32_t dataOffset;
} BitmapFileHeader;

typedef struct
{
    int32_t headerSize;
    int32_t width;
    int32_t height;
    int16_t planes;
    int16_t bitsPerPixel;
    int32_t compression;
    int32_t dataSize;
    int32_t horizontalResolution;
    int32_t verticalResolution;
    int32_t colors;
    int32_t importantColors;
} BitmapInfoHeader;

typedef struct
{
    BitmapFileHeader h_bmp;
    BitmapInfoHeader h_info;
    uint8_t*         pixel;
} Bitmap;

    Bitmap* bmp_create(unsigned w, unsigned h);
    Bitmap* bmp_destroy(Bitmap* bmp);
    int     bmp_fill(Bitmap*, uint8_t, uint8_t, uint8_t);
    int     bmp_grad(Bitmap* bmp);
    Bitmap* bmp_read(const char* filename);
    int     bmp_setPixel(
            Bitmap* bmp, int x, int y, uint8_t red, uint8_t green,
            uint8_t blue);
    int bmp_write(Bitmap* bmp, const char* filename);

#pragma pack(pop)

一个可能的bitmap.c完整实现

#pragma pack(push, 2)
#pragma pack(show)
#include "bitmap.h"

Bitmap* bmp_create(unsigned w, unsigned h)
{
    Bitmap* one = (Bitmap*)calloc(1, sizeof(Bitmap));

    one->h_bmp.dataOffset =
        sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);
    one->h_bmp.fileSize =
        one->h_bmp.dataOffset + (w * h * 4);
    // fprintf(
    //     stderr, "\tbmp_create(): file size is %u\n",
    //     one->h_bmp.fileSize);
    one->h_bmp.header[0] = 'B';
    one->h_bmp.header[1] = 'M';

    one->h_info.bitsPerPixel         = 32;
    one->h_info.headerSize           = 40;
    one->h_info.height               = h;
    one->h_info.horizontalResolution = 2400;
    one->h_info.planes               = 1;
    one->h_info.verticalResolution   = 2400;
    one->h_info.width                = w;
    one->pixel                       = (uint8_t*)calloc(
        1, (size_t)(w * h * 4 * sizeof(uint8_t)));
    if (one->pixel == NULL) return NULL;
    return one;
};

Bitmap* bmp_destroy(Bitmap* bmp)
{
    if (bmp == NULL) return NULL;
    if (bmp->pixel != NULL) free(bmp->pixel);
    free(bmp);
    fprintf(stderr, "\tbitmap destroyed\n");
    return NULL;
}

int bmp_fill(
    Bitmap* bmp, uint8_t red, uint8_t green, uint8_t blue)
{
    if (bmp == NULL) return -1;
    uint8_t* p = bmp->pixel;
    for (int32_t i = 0;
         i < bmp->h_info.width * bmp->h_info.height; i += 1)
    {  // 32bpp
        *p++ = blue;
        *p++ = green;
        *p++ = red;
        *p++ = 0xFF;  // alpha
    }
    return 0;
}

int bmp_grad(Bitmap* bmp)
{
    // fill screen with 3 vertical stripes R G B
    // color ranging from 0 to 255 in each line
    if (bmp == NULL) return -1;
    uint8_t  red   = 0;
    uint8_t  green = 0;
    uint8_t  blue  = 0;
    uint8_t* p     = bmp->pixel;
    for (int y = 0; y < bmp->h_info.height; y++)
    {
        uint8_t color_value =
            (uint8_t)(255 * (double)y / bmp->h_info.height);
        for (int x = 0; x < bmp->h_info.width; x++)
        {
            switch (x / (bmp->h_info.width / 3))
            {
                case 0:
                    red   = color_value;
                    green = 0;
                    blue  = 0;
                    break;
                case 1:
                    red   = 0;
                    green = color_value;
                    blue  = 0;
                    break;
                default:
                    red   = 0;
                    green = 0;
                    blue  = color_value;
                    break;
            }
            *p++ = blue;
            *p++ = green;
            *p++ = red;
            *p++ = 0XFF;
        }
    };
    return 0;
}

Bitmap* bmp_read(const char* filename)
{
    FILE* in = fopen(filename, "rb");
    if (in == NULL) return NULL;
    // any size is ok
    Bitmap* one = bmp_create(800, 600);
    if (one == NULL)
    {
        fclose(in);
        return NULL;
    }
    size_t size =
        sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);
    size_t res = fread((void*)one, (size_t)(1), size, in);
    if (res != size) return NULL;
    // read ok: now adjust the pixels and H W to the actual
    // values of this file
    size = one->h_info.height * one->h_info.width *
           4;  // 32bpp
    uint8_t* area = (uint8_t*)malloc(size);
    if (area == NULL)
    {
        fclose(in);
        return (NULL);
    };
    free(one->pixel);
    one->pixel = area;
    res = fread((void*)one->pixel, (size_t)(1), size, in);
    return one;
}

int bmp_setPixel(Bitmap* bmp,
    int x, int y, uint8_t red, uint8_t green, uint8_t blue)
{
    if (bmp == NULL) return -1;
    uint8_t* pPixel = bmp->pixel;
    // get address of coordinates of x,y
    pPixel += (y * 4) * bmp->h_info.width + (x * 4);
    pPixel[0] = blue;
    pPixel[1] = green;
    pPixel[2] = red;
    pPixel[3] = 0xFF;
    return 0;
}

int bmp_write(Bitmap* bmp, const char* filename)
{
    FILE* out = fopen(filename, "wb");
    if (out == NULL) return -1;
    size_t res = 0;
    res        = fwrite(
        &bmp->h_bmp, (size_t)1, sizeof(BitmapFileHeader),
        out);
    res = fwrite(
        &bmp->h_info, (size_t)1, sizeof(BitmapInfoHeader),
        out);
    size_t size =
        (bmp->h_info.width * bmp->h_info.height * 4);
    res = fwrite(
        (const void*)bmp->pixel, (size_t)(1), size, out);
    if ( size != res )
    { 
        fclose(out);
        fprintf(stderr, "\tbmp_write() write %zd. %zd expected\n",res, size);
        fclose(out);
        return -2;    }
    res = fclose(out);
    return 0;
}

#pragma pack(pop)

注意:在阅读和写入位图数据时,不要忘记将结构体与字边界对齐。它只是在编译器中使用#pragma或类似的设置

本例输出

bitmap created ok!
        bmp_fill returned 0
        bmp_write() returned 0
        bmp_fill() returned 0
        bmp_write() returned 0
        bmp_fill() returned 0
        bmp_write() returned 0
        bmp_read() read a [1024 x 768], size is 3145782
        bmp_write() returned 0
        bmp_grad() returned 0
        bmp_write() returned 0
        bmp_write() returned 0
        bitmap destroyed
        bitmap destroyed
        [end of tests]

一些位图将被写入磁盘。

为什么这样可能更容易?

例如,bmp_write()代码

int bmp_write(Bitmap* bmp, const char* filename)
{
    FILE* out = fopen(filename, "wb");
    if (out == NULL) return -1;
    size_t res = 0;
    res        = fwrite(
        &bmp->h_bmp, (size_t)1, sizeof(BitmapFileHeader),
        out);
    res = fwrite(
        &bmp->h_info, (size_t)1, sizeof(BitmapInfoHeader),
        out);
    size_t size = (bmp->h_info.width * bmp->h_info.height * 4);
    res = fwrite(
        (const void*)bmp->pixel, size,
        (size_t)(1), out);
    res = fclose(out);
    return 0;
}

封装位图使事情更容易编写,测试和阅读。

bmp_setPixel作为(另一个)示例

int bmp_setPixel(Bitmap* bmp,
    int x, int y, uint8_t red, uint8_t green, uint8_t blue)
{
    if (bmp == NULL) return -1;
    uint8_t* pPixel = bmp->pixel;
    // get address of coordinates of x,y
    pPixel += (y * 4) * bmp->h_info.width + (x * 4);
    pPixel[0] = blue;
    pPixel[1] = green;
    pPixel[2] = red;
    pPixel[3] = 0xFF;
    return 0;
}

用这种方式编写可以很容易地在位图上构建绘图原语。理解这一点的关键是要看到,

pPixel += (y * 4) * bmp->h_info.width + (x * 4);

(x,y)位图坐标转换为struct中的像素地址

hlswsv35

hlswsv352#

你有这么多层次的间接.
(*pArr)[i][j]是一个指向一维数组的指针。
您可以访问整个结构

(*(*pArr))[i][j] 
(**pArr)[i][j] 
(*pArr)[0][i][j]
pArr[0][0][i][j]

fread中:

fread(&(*(*pArr))[i][i], sizeof((*(*pArr))[i][i]), 1, file);
    fread(&(**pArr)[i][j],   sizeof((**pArr)[i][j]),   1, file);
    fread(&(*pArr)[0][i][i], sizeof((*pArr)[0][i][i]), 1, file);
    fread(&pArr[0][0][i][i], sizeof(pArr[0][0][i][i]), 1, file);

或访问特定成员

fread(&(*(*pArr))[i][i].r, sizeof((*(*pArr))[i][i].r), 1, file);
    fread(&(**pArr)[i][j].r,   sizeof((**pArr)[i][j].r),   1, file);
    fread(&(*pArr)[0][i][i].r, sizeof((*pArr)[0][i][i].r), 1, file);
    fread(&pArr[0][0][i][i].r, sizeof(pArr[0][0][i][i].r), 1, file);

使用对象而不是sizeof中的类型。例如,如果您将成员更改为另一种类型,则转换器将自动更改它。

相关问题