通过指针将在一个函数中初始化的结构示例传递到C中的另一个函数,使用GCC会产生不同的结果

im9ewurl  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(76)

(我这样做是为了一个个人项目--只是为了学习指针/引用/内存管理之类的东西如何在C语言中工作。我想“不作弊”,也就是说。而不需要任何超级抽象库或语言的帮助。因此,C语言是首选语言。)
很简单,我试图将位图文件的头与原始数据一起读取到传递函数中的结构示例中。首选工具-ChatGPT和GCC推荐的fread(),因为这是您通常在Linux Ubuntu终端上使用的工具。
下面是传统的54字节头BMP格式的代码(注意字节填充问题已经处理好了):

#ifndef BMPHEADER_H
#define BMPHEADER_H
#include <stdint.h> // For fixed-width types                                                                                                                  

#pragma pack(push, 1) // Ensure that structure is packed without padding                                                                                      
typedef struct {
  uint16_t signature; // BM                                                                                                                                   
  uint32_t fileSize;
  uint16_t reserved1;
  uint16_t reserved2;
  uint32_t dataOffset;
  uint32_t headerSize;
  int32_t width;
  int32_t height;
  uint16_t planes;
  uint16_t bitsPerPixel;
  uint32_t compression;
  uint32_t rawBmpSize;
  uint32_t horizRes;
  uint32_t vertRes;
  uint32_t numColors;
  uint32_t numImptColors;
} BmpHeader;
#pragma pack(pop)

#endif

下面是示例化包含头的结构并提供令人满意的结果的代码:

void run_core_funcs(void)
{
    printf("Running core OCR and I/O functions ...\n");

    ProcList pm;
    BmpHeader bh;
    get_fname(pm.fname);

    FILE * src = fopen(pm.fname, "rb");
    fread(&bh, sizeof(bh), 1, src);
    printf("Sig: %x \n", bh.signature);
    printf("File size: %x \n", bh.fileSize);
    printf("Res 1: %x \n", bh.reserved1);
    printf("Res 2: %x \n", bh.reserved2);
    printf("Offset: %x \n", bh.dataOffset);
    fclose(src);

    get_img_props(&pm, &bh);

注意,我调用了一个不同的函数,并通过它们的指针传递处理规范管理器和位图头(因为C没有通过引用传递的构造)。这就是事情变得有趣的地方。

void get_img_props(ProcList * pm, BmpHeader * imgHdr)
{
  printf("Caching image data from %s ...\n", pm->fname);

  FILE * src = fopen(pm->fname, "rb");
  fread(imgHdr, sizeof(imgHdr), 1, src);
  printf("Sig: %x \n", imgHdr->signature);
  printf("File size: %x \n", imgHdr->fileSize);
  printf("Res 1: %x \n", imgHdr->reserved1);
  printf("Res 2: %x \n", imgHdr->reserved2);
  printf("Offset: %x \n", imgHdr->dataOffset);
  fclose(src);

是的,重复的密码,只是为了确保一切都是合法的。
下面是“外部”和“内部”功能的满意结果。(是的,我意识到这些字段表明文件大小、数据偏移量和信息头大小应该非常大,但这不是重点。

Sig: 4d42 
File size: 7e98a 
Res 1: 0 
Res 2: 0 
Offset: 8a

然而,......一旦我在run_core_funcs()中注解掉原始数据检索代码,通过指针传递结构体示例,并仅在get_img_props()中执行原始数据检索,我就会得到不同的东西:
结果如下:

Sig: 4d42 
File size: 7e98a 
Res 1: 0 
Res 2: 3 
Offset: 0

为什么会这样?我不太愿意相信我做错了什么,而更愿意相信C中指针的工作方式或GCC处理事情的方式存在固有的错误实现。但我知道什么。
我在不同的平台(GoogleSOthis Reddit one is not particularly helpful)上做了一些研究,我得到的唯一的东西是关于指针与引用、内存开销等。ChatGPT也没有真正帮上什么忙,用不同的要点来说明这都是我的错,我应该检查我清晰的工作中的错误,并考虑我已经考虑过的所有问题-将地址传递给结构对象的质量,数据对齐要求等。还有提醒:我用的是GCC。

gzjq41n4

gzjq41n41#

void get_img_props(ProcList * pm, BmpHeader * imgHdr) {
   // ...
   fread(imgHdr, sizeof(imgHdr), 1, src);

sizeof(imgHdr)是指针的大小。所以你只读了阅读8字节(假设是64位系统),而不是54字节。
你想要sizeof *imgHdr或者sizeof(BmpHeader)
当您在外部函数中填充结构时,您没有注意到内部函数中的问题,因为最后46个字节已经包含了之前填充时的正确数据。但如果不这样做,这46个字节完全未初始化,并且包含垃圾。
检查fread和所有其他系统/库函数的返回值,这通常是必不可少的,可以帮助您更快地找到它。我怀疑valgrind也会发现未初始化内存的使用。
我不太愿意相信我做错了什么,而不是[…]
Obligatory ;-)

相关问题