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

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

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

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

在我的readPixelsBMP函数中。和

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

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. struct BMP_Header {
  4. char signature[2];
  5. int size;
  6. short reserved1;
  7. short reserved2;
  8. int offset_pixel_array;
  9. };
  10. struct DIB_Header{
  11. int size;
  12. int width;
  13. int height;
  14. short planes;
  15. short bits_per_pixel;
  16. int compression;
  17. int image_size;
  18. int x_per_meter;
  19. int y_per_meter;
  20. int colors_in_table;
  21. int important_colors;
  22. };
  23. struct Pixel{
  24. unsigned char r;
  25. unsigned char b;
  26. unsigned char g;
  27. };
  28. void readBMPHeader(FILE* file, struct BMP_Header* header) {
  29. fread(&header->signature, sizeof(char) *2, 1,file);
  30. fread(&header->size, sizeof(int), 1,file);
  31. fread(&header->reserved1, sizeof(short), 1,file);
  32. fread(&header->reserved2, sizeof(short), 1,file);
  33. fread(&header->offset_pixel_array, sizeof(int), 1,file);
  34. }
  35. void readDIBHeader(FILE* file, struct DIB_Header* header) {
  36. fread(&header->size, sizeof(int), 1,file);
  37. fread(&header->width, sizeof(int), 1,file);
  38. fread(&header->height, sizeof(int), 1,file);
  39. fread(&header->planes, sizeof(short), 1,file);
  40. fread(&header->bits_per_pixel, sizeof(short), 1,file);
  41. fread(&header->compression, sizeof(int), 1,file);
  42. fread(&header->image_size, sizeof(int), 1,file);
  43. fread(&header->x_per_meter, sizeof(int), 1,file);
  44. fread(&header->y_per_meter, sizeof(int), 1,file);
  45. fread(&header->colors_in_table, sizeof(int), 1,file);
  46. fread(&header->important_colors, sizeof(int), 1,file);
  47. }
  48. void readPixelsBMP(FILE* file, int width, int height, struct Pixel (**pArr)[width][height]) {
  49. *pArr = malloc( sizeof(struct Pixel[width][height]) );
  50. if (*pArr == NULL) {
  51. printf("Error! Insufficient memory!");
  52. return;
  53. }
  54. int padding = (4 - ((3*width) % 4)) % 4;
  55. for (int i = 0; i < width; i++) {
  56. for (int j = 0; j < height; j++) {
  57. //fread(*pArr, sizeof(struct Pixel), 1, file);
  58. fread(&(*pArr)[i][j]->b, sizeof(unsigned char), 1, file);
  59. //fread((*pArr)[i][j]->r, sizeof(unsigned char), 1, file);
  60. //fread((*pArr)[i][j]->g, sizeof(unsigned char), 1, file);
  61. }
  62. if (padding) {
  63. fseek(file, padding, SEEK_CUR);
  64. }
  65. }
  66. fclose(file);
  67. }
  68. void writeBMPHeader(FILE* file, struct BMP_Header* header) {
  69. fwrite(&header->signature, sizeof(char) *2, 1,file);
  70. fwrite(&header->size, sizeof(int), 1,file);
  71. fwrite(&header->reserved1, sizeof(short), 1,file);
  72. fwrite(&header->reserved2, sizeof(short), 1,file);
  73. fwrite(&header->offset_pixel_array, sizeof(int), 1,file);
  74. }
  75. void writeDIBHeader(FILE* file, struct DIB_Header* header) {
  76. fwrite(&header->size, sizeof(int), 1,file);
  77. fwrite(&header->width, sizeof(int), 1,file);
  78. fwrite(&header->height, sizeof(int), 1,file);
  79. fwrite(&header->planes, sizeof(short), 1,file);
  80. fwrite(&header->bits_per_pixel, sizeof(short), 1,file);
  81. fwrite(&header->compression, sizeof(int), 1,file);
  82. fwrite(&header->image_size, sizeof(int), 1,file);
  83. fwrite(&header->x_per_meter, sizeof(int), 1,file);
  84. fwrite(&header->y_per_meter, sizeof(int), 1,file);
  85. fwrite(&header->colors_in_table, sizeof(int), 1,file);
  86. fwrite(&header->important_colors, sizeof(int), 1,file);
  87. }
  88. void writePixelsBMP(FILE* file, int width, int height, struct Pixel (**pArr)[width][height]) {
  89. int padding = (4 - ((3*width) % 4)) % 4;
  90. for (int i = 0; i < width; i++) {
  91. for (int j = 0; j < height; j++) {
  92. fwrite(*pArr, sizeof(struct Pixel), 1, file);
  93. }
  94. if (padding)
  95. fwrite("0", sizeof(char),padding, file);
  96. }
  97. char eof = 0x00;
  98. fwrite(&eof, sizeof(char), 1, file);
  99. }
  100. int main() {
  101. char* filename = "test1wonderbread.bmp";
  102. char* output = "test3wonderbread.bmp";
  103. FILE* file_input = fopen(filename, "rb");
  104. FILE* file_output = fopen(output, "wb");
  105. struct BMP_Header* header = (struct BMP_Header*) malloc(sizeof(struct BMP_Header));
  106. readBMPHeader(file_input, header);
  107. struct DIB_Header* dib_header = (struct DIB_Header*) malloc(sizeof(struct DIB_Header));
  108. readDIBHeader(file_input, dib_header);
  109. struct Pixel (*pArr)[dib_header->width][dib_header->height];
  110. readPixelsBMP(file_input, dib_header->width, dib_header->height, &pArr);
  111. writeBMPHeader(file_output, header);
  112. writeDIBHeader(file_output, dib_header);
  113. //writePixelsBMP(file_output, dib_header->width, dib_header->height, &pArr);
  114. printf("no errors");
  115. return 0;
  116. }
mlnl4t2r

mlnl4t2r1#

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

所有这些都是54字节?.

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

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

完全可以无视。

一个位图struct和几种方法

  1. typedef struct
  2. {
  3. char header[2];
  4. int32_t fileSize;
  5. int32_t reserved;
  6. int32_t dataOffset;
  7. } BitmapFileHeader;
  8. typedef struct
  9. {
  10. int32_t headerSize;
  11. int32_t width;
  12. int32_t height;
  13. int16_t planes;
  14. int16_t bitsPerPixel;
  15. int32_t compression;
  16. int32_t dataSize;
  17. int32_t horizontalResolution;
  18. int32_t verticalResolution;
  19. int32_t colors;
  20. int32_t importantColors;
  21. } BitmapInfoHeader;
  22. typedef struct
  23. {
  24. BitmapFileHeader h_bmp;
  25. BitmapInfoHeader h_info;
  26. uint8_t* pixel;
  27. } Bitmap;
  28. Bitmap* bmp_create(unsigned w, unsigned h);
  29. Bitmap* bmp_destroy(Bitmap* bmp);
  30. int bmp_fill(Bitmap*, uint8_t, uint8_t, uint8_t);
  31. int bmp_grad(Bitmap* bmp);
  32. Bitmap* bmp_read(const char* filename);
  33. int bmp_setPixel(
  34. Bitmap* bmp, int x, int y, uint8_t red, uint8_t green,
  35. uint8_t blue);
  36. int bmp_write(Bitmap* bmp, const char* filename);

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

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

main()进行测试

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

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

main.c代码

  1. #include "bitmap.h"
  2. int main(void)
  3. {
  4. Bitmap* a_test = bmp_create(1024, 768);
  5. if (a_test == NULL)
  6. {
  7. fprintf(stderr, "\tbitmap not created\n");
  8. return -1;
  9. }
  10. fprintf(stderr, "\tbitmap created ok!\n");
  11. int res = 0;
  12. res = bmp_fill(a_test, 255, 0, 0); // red
  13. fprintf(stderr, "\tbmp_fill returned %d\n", res);
  14. res = bmp_write(a_test, "red.bmp");
  15. fprintf(stderr, "\tbmp_write() returned %d\n", res);
  16. res = bmp_fill(a_test, 0, 255, 0); // green
  17. fprintf(stderr, "\tbmp_fill() returned %d\n", res);
  18. res = bmp_write(a_test, "green.bmp");
  19. fprintf(stderr, "\tbmp_write() returned %d\n", res);
  20. res = bmp_fill(a_test, 0, 0, 255); // blue
  21. fprintf(stderr, "\tbmp_fill() returned %d\n", res);
  22. res = bmp_write(a_test, "blue.bmp");
  23. fprintf(stderr, "\tbmp_write() returned %d\n", res);
  24. Bitmap* a_copy = bmp_read("blue.bmp");
  25. fprintf(stderr, "\tbmp_read() read a [%d x %d], size is %d\n",
  26. a_copy->h_info.width,
  27. a_copy->h_info.height,
  28. a_copy->h_bmp.fileSize);
  29. res = bmp_write(a_copy, "blueCopy.bmp");
  30. fprintf(stderr, "\tbmp_write() returned %d\n", res);
  31. res = bmp_grad(a_copy);
  32. fprintf(stderr, "\tbmp_grad() returned %d\n", res);
  33. res = bmp_write(a_copy, "stripes.bmp");
  34. fprintf(stderr, "\tbmp_write() returned %d\n", res);
  35. // draw a black line at the center of the bitmap
  36. for (int i = 0; i < a_copy->h_info.width; i += 1)
  37. for (int j = (a_copy->h_info.height / 2) - 5;
  38. j < (a_copy->h_info.height / 2) + 5; j+=1)
  39. bmp_setPixel(a_copy, i, j, 0, 0, 0);
  40. res = bmp_write(a_copy, "lineset_Pixel.bmp");
  41. fprintf(stderr, "\tbmp_write() returned %d\n", res);
  42. a_test = bmp_destroy(a_test);
  43. a_copy = bmp_destroy(a_copy);
  44. fprintf(stderr, "\t[end of tests]\n");
  45. return 0;
  46. }

Bitmap.h

  1. #pragma once
  2. #pragma pack(push, 2)
  3. #pragma pack(show)
  4. #include <stdint.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. typedef struct
  8. {
  9. char header[2];
  10. int32_t fileSize;
  11. int32_t reserved;
  12. int32_t dataOffset;
  13. } BitmapFileHeader;
  14. typedef struct
  15. {
  16. int32_t headerSize;
  17. int32_t width;
  18. int32_t height;
  19. int16_t planes;
  20. int16_t bitsPerPixel;
  21. int32_t compression;
  22. int32_t dataSize;
  23. int32_t horizontalResolution;
  24. int32_t verticalResolution;
  25. int32_t colors;
  26. int32_t importantColors;
  27. } BitmapInfoHeader;
  28. typedef struct
  29. {
  30. BitmapFileHeader h_bmp;
  31. BitmapInfoHeader h_info;
  32. uint8_t* pixel;
  33. } Bitmap;
  34. Bitmap* bmp_create(unsigned w, unsigned h);
  35. Bitmap* bmp_destroy(Bitmap* bmp);
  36. int bmp_fill(Bitmap*, uint8_t, uint8_t, uint8_t);
  37. int bmp_grad(Bitmap* bmp);
  38. Bitmap* bmp_read(const char* filename);
  39. int bmp_setPixel(
  40. Bitmap* bmp, int x, int y, uint8_t red, uint8_t green,
  41. uint8_t blue);
  42. int bmp_write(Bitmap* bmp, const char* filename);
  43. #pragma pack(pop)

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

  1. #pragma pack(push, 2)
  2. #pragma pack(show)
  3. #include "bitmap.h"
  4. Bitmap* bmp_create(unsigned w, unsigned h)
  5. {
  6. Bitmap* one = (Bitmap*)calloc(1, sizeof(Bitmap));
  7. one->h_bmp.dataOffset =
  8. sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);
  9. one->h_bmp.fileSize =
  10. one->h_bmp.dataOffset + (w * h * 4);
  11. // fprintf(
  12. // stderr, "\tbmp_create(): file size is %u\n",
  13. // one->h_bmp.fileSize);
  14. one->h_bmp.header[0] = 'B';
  15. one->h_bmp.header[1] = 'M';
  16. one->h_info.bitsPerPixel = 32;
  17. one->h_info.headerSize = 40;
  18. one->h_info.height = h;
  19. one->h_info.horizontalResolution = 2400;
  20. one->h_info.planes = 1;
  21. one->h_info.verticalResolution = 2400;
  22. one->h_info.width = w;
  23. one->pixel = (uint8_t*)calloc(
  24. 1, (size_t)(w * h * 4 * sizeof(uint8_t)));
  25. if (one->pixel == NULL) return NULL;
  26. return one;
  27. };
  28. Bitmap* bmp_destroy(Bitmap* bmp)
  29. {
  30. if (bmp == NULL) return NULL;
  31. if (bmp->pixel != NULL) free(bmp->pixel);
  32. free(bmp);
  33. fprintf(stderr, "\tbitmap destroyed\n");
  34. return NULL;
  35. }
  36. int bmp_fill(
  37. Bitmap* bmp, uint8_t red, uint8_t green, uint8_t blue)
  38. {
  39. if (bmp == NULL) return -1;
  40. uint8_t* p = bmp->pixel;
  41. for (int32_t i = 0;
  42. i < bmp->h_info.width * bmp->h_info.height; i += 1)
  43. { // 32bpp
  44. *p++ = blue;
  45. *p++ = green;
  46. *p++ = red;
  47. *p++ = 0xFF; // alpha
  48. }
  49. return 0;
  50. }
  51. int bmp_grad(Bitmap* bmp)
  52. {
  53. // fill screen with 3 vertical stripes R G B
  54. // color ranging from 0 to 255 in each line
  55. if (bmp == NULL) return -1;
  56. uint8_t red = 0;
  57. uint8_t green = 0;
  58. uint8_t blue = 0;
  59. uint8_t* p = bmp->pixel;
  60. for (int y = 0; y < bmp->h_info.height; y++)
  61. {
  62. uint8_t color_value =
  63. (uint8_t)(255 * (double)y / bmp->h_info.height);
  64. for (int x = 0; x < bmp->h_info.width; x++)
  65. {
  66. switch (x / (bmp->h_info.width / 3))
  67. {
  68. case 0:
  69. red = color_value;
  70. green = 0;
  71. blue = 0;
  72. break;
  73. case 1:
  74. red = 0;
  75. green = color_value;
  76. blue = 0;
  77. break;
  78. default:
  79. red = 0;
  80. green = 0;
  81. blue = color_value;
  82. break;
  83. }
  84. *p++ = blue;
  85. *p++ = green;
  86. *p++ = red;
  87. *p++ = 0XFF;
  88. }
  89. };
  90. return 0;
  91. }
  92. Bitmap* bmp_read(const char* filename)
  93. {
  94. FILE* in = fopen(filename, "rb");
  95. if (in == NULL) return NULL;
  96. // any size is ok
  97. Bitmap* one = bmp_create(800, 600);
  98. if (one == NULL)
  99. {
  100. fclose(in);
  101. return NULL;
  102. }
  103. size_t size =
  104. sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);
  105. size_t res = fread((void*)one, (size_t)(1), size, in);
  106. if (res != size) return NULL;
  107. // read ok: now adjust the pixels and H W to the actual
  108. // values of this file
  109. size = one->h_info.height * one->h_info.width *
  110. 4; // 32bpp
  111. uint8_t* area = (uint8_t*)malloc(size);
  112. if (area == NULL)
  113. {
  114. fclose(in);
  115. return (NULL);
  116. };
  117. free(one->pixel);
  118. one->pixel = area;
  119. res = fread((void*)one->pixel, (size_t)(1), size, in);
  120. return one;
  121. }
  122. int bmp_setPixel(Bitmap* bmp,
  123. int x, int y, uint8_t red, uint8_t green, uint8_t blue)
  124. {
  125. if (bmp == NULL) return -1;
  126. uint8_t* pPixel = bmp->pixel;
  127. // get address of coordinates of x,y
  128. pPixel += (y * 4) * bmp->h_info.width + (x * 4);
  129. pPixel[0] = blue;
  130. pPixel[1] = green;
  131. pPixel[2] = red;
  132. pPixel[3] = 0xFF;
  133. return 0;
  134. }
  135. int bmp_write(Bitmap* bmp, const char* filename)
  136. {
  137. FILE* out = fopen(filename, "wb");
  138. if (out == NULL) return -1;
  139. size_t res = 0;
  140. res = fwrite(
  141. &bmp->h_bmp, (size_t)1, sizeof(BitmapFileHeader),
  142. out);
  143. res = fwrite(
  144. &bmp->h_info, (size_t)1, sizeof(BitmapInfoHeader),
  145. out);
  146. size_t size =
  147. (bmp->h_info.width * bmp->h_info.height * 4);
  148. res = fwrite(
  149. (const void*)bmp->pixel, (size_t)(1), size, out);
  150. if ( size != res )
  151. {
  152. fclose(out);
  153. fprintf(stderr, "\tbmp_write() write %zd. %zd expected\n",res, size);
  154. fclose(out);
  155. return -2; }
  156. res = fclose(out);
  157. return 0;
  158. }
  159. #pragma pack(pop)

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

本例输出

  1. bitmap created ok!
  2. bmp_fill returned 0
  3. bmp_write() returned 0
  4. bmp_fill() returned 0
  5. bmp_write() returned 0
  6. bmp_fill() returned 0
  7. bmp_write() returned 0
  8. bmp_read() read a [1024 x 768], size is 3145782
  9. bmp_write() returned 0
  10. bmp_grad() returned 0
  11. bmp_write() returned 0
  12. bmp_write() returned 0
  13. bitmap destroyed
  14. bitmap destroyed
  15. [end of tests]

一些位图将被写入磁盘。

为什么这样可能更容易?

例如,bmp_write()代码

  1. int bmp_write(Bitmap* bmp, const char* filename)
  2. {
  3. FILE* out = fopen(filename, "wb");
  4. if (out == NULL) return -1;
  5. size_t res = 0;
  6. res = fwrite(
  7. &bmp->h_bmp, (size_t)1, sizeof(BitmapFileHeader),
  8. out);
  9. res = fwrite(
  10. &bmp->h_info, (size_t)1, sizeof(BitmapInfoHeader),
  11. out);
  12. size_t size = (bmp->h_info.width * bmp->h_info.height * 4);
  13. res = fwrite(
  14. (const void*)bmp->pixel, size,
  15. (size_t)(1), out);
  16. res = fclose(out);
  17. return 0;
  18. }

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

bmp_setPixel作为(另一个)示例

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

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

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

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

展开查看全部
hlswsv35

hlswsv352#

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

  1. (*(*pArr))[i][j]
  2. (**pArr)[i][j]
  3. (*pArr)[0][i][j]
  4. pArr[0][0][i][j]

fread中:

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

或访问特定成员

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

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

展开查看全部

相关问题