Passing by pointer a struct instance initialized within one function on to another function in C yields different results using GCC

(I’m doing this for a personal project – just to learn how things like pointers/references/memory management work with C language. I want to do this "without cheating", i.e. without any aid of super-abstracting libraries or languages. Hence C as the language of choice.)

Quite simply, I’m trying to read a bitmap file’s header with raw data into a struct instance in a passed-to function. Tools of choice – fread() as recommended by ChatGPT and GCC ’cause that’s what you typically use on a Linux Ubuntu terminal.

Below is the code for the the conventional 54-byte-header BMP format (note that the byte padding matter is taken care of):

#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

Below is the code that instantiates the header-containing struct and delivers satisfactory results:

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);

Notice that I’m calling a different function and passing the processing-specs manager and the bitmap header by their pointers (since C has no construct for passing by reference). This is where things get interesting.

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);

Yes, duplicate code, just to be sure all’s kosher.

Below are the satisfactory results from both the "outer" and the "inner" functions. (Yes, I realize that the fields indicate that the file size, data offset, and the info header size are expected to be really large, but that is not the point.)

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

However, … once I comment out the raw data retrieval code in run_core_funcs(), pass the struct instances by their pointers, and perform raw data retrieval only in get_img_props(), I’m getting something different:

Results:

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

Why is that? I’m less inclined to believe that I’m doing something wrong than I’m inclined to believe that there is an inherently buggy implementation of how pointers in C work or how GCC handles things. But what do I know.

I did some research across different platforms (Google, SO, this Reddit one is not particularly helpful), and the only things I’m getting are generic hits pertaining to confusion over pointers vs. references, concern with memory overhead, etc. ChatGPT didn’t really do any favors either, pitching with variations of different bullet points that it’s all my fault and that I should check my crystal-clear work for bugs and account for all that I’ve already accounted for – quality of passing the addresses to the struct objects, data alignment requirements, etc. And a reminder: I am using GCC.

>Solution :

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

sizeof(imgHdr) is the size of a pointer. So you’re only reading 8 bytes (assuming a 64-bit system), not 54.

You want sizeof *imgHdr or else sizeof(BmpHeader).

When you populated the struct in the outer function, you didn’t notice the issue in the inner function, because the last 46 bytes already contained the correct data from when it was populated previously. But when you don’t, those 46 bytes are totally uninitialized and contain garbage.

Checking the return value of fread and all other system/library functions, which is essential in general, would have helped you find this faster. I suspect valgrind would also have caught the use of uninitialized memory.

I’m less inclined to believe that I’m doing something wrong than […]

Obligatory 😉

Leave a Reply