保持GCC不改变结构的大小(.BMP文件头)

Big*_*olf 4 c gcc struct bmp

我正在尝试创建.BMP文件.根据我的定义,BMP头是54个字节长.代码编译和一切,但在尝试打开文件时,我得到"错误的标题格式"错误.

如果我做一个sizeof(structtype)我得到56个字节而不是定义的54 - 如果我用值初始化一个结构,然后做一个sizeof(newStruct),我得到8个字节.因为我需要将54个字节写入文件,所以这很糟糕.

有没有办法让GCC不以这种方式改变结构大小?

这是结构的定义:

typedef struct
{
  uint16_t typeSignature; // = "BM"
  uint32_t filesize;     //filesize in Bytes
  uint32_t reserved;     // = 0 for this program
  uint32_t headerOffset; // = 54
} BmpFileHeader;

typedef struct
{
  uint32_t infoHeaderSize;    //size of header in byte. ( = 40)
  uint32_t Width;             //width of file in pixels
  uint32_t Height;            // height of file in pixels

  uint16_t Colors;       //colorbits per pixel (24 for 3byte RGB)
  uint16_t bitsPerPixel;
  uint32_t Compression;       //compression mode; 0 for uncompressed.
  uint32_t SizeImg;         //if biCompress = 0, =0. Else: filesize.

  uint32_t xPelsPerMeter;     // for output device;
  uint32_t yPelsPerMeter;     // 0 for this program

  uint32_t ColorsUsed;      // Colours used; = 0 for all
  uint32_t ColorsImportant; // num. of used colours, 0 for all
} BmpInfoHeader;

typedef struct
{
  BmpFileHeader fileheader;
  BmpInfoHeader infoheader;
} bitmapHead; // sizeof = 56
Run Code Online (Sandbox Code Playgroud)

这里初始化一个新标题的函数:

bitmapHead* assembleHeader(int compCount)
{
  bitmapHead* newHeader = (bitmapHead*) calloc(1, 54);  
  newHeader->fileheader.typeSignature = 0x4D42;
  newHeader->fileheader.filesize = (compCount*100*51*3 + 54);
  newHeader->fileheader.reserved = 0;
  newHeader->fileheader.headerOffset = 54;


  newHeader->infoheader.infoHeaderSize = 40;
  newHeader->infoheader.Width = 100*compCount;
  newHeader->infoheader.Height = 51;
  newHeader->infoheader.Colors = 1; 
  newHeader->infoheader.bitsPerPixel = 21;
  newHeader->infoheader.Compression = 0;
  newHeader->infoheader.SizeImg = compCount*100*51*3;
  newHeader->infoheader.xPelsPerMeter = 0;
  newHeader->infoheader.yPelsPerMeter = 0;
  newHeader->infoheader.ColorsUsed = 0;
  newHeader->infoheader.ColorsImportant = 0;
  printf("%lu \n", sizeof(newHeader)); // This gives me 8. 
  return newHeader;
}
Run Code Online (Sandbox Code Playgroud)

zwo*_*wol 6

这是"不要那样做"的情况.具体而言,永远不要尝试布局struct与外部规范定义的字节模式匹配的C. C标准对与此合作并不感兴趣,虽然有些编译器试图使其成为可能,但它比它的价值更麻烦.(阅读Ada表示条款,如果您有兴趣了解认真解决这个问题的语言.)

您可以定义a struct来表示内存中的BMP头,但是您从磁盘读取/写入的内容应该是一个数组uint8_t.雅莉.您必须编写函数以在内存中表示和磁盘表示之间显式转换.这些也是进行任何必要检查和处理字节序的好地方.

BmpFileHeader的工作示例:

// Fixed fields not represented in in-memory header.
typedef struct
{
    uint32_t filesize;     // total size of file in bytes
    uint32_t headerOffset; // offset from beginning of file to end of headers
                           // (normally 54)
} BmpFileHeader;

#define BMP_FILE_HEADER_MAGIC_1  0
#define BMP_FILE_HEADER_MAGIC_2  1
#define BMP_FILE_HEADER_FILESIZE 2
#define BMP_FILE_HEADER_RESERVED 6
#define BMP_FILE_HEADER_HDROFF   10
#define BMP_FILE_HEADER_SIZE     14
typedef uint8_t BmpFileHeaderOnDisk[BMP_FILE_HEADER_SIZE];

uint32_t
le32_to_cpu(uint8_t *p)
{
    return ((((uint32_t)p[0]) <<  0) | 
            (((uint32_t)p[1]) <<  8) |
            (((uint32_t)p[2]) << 16) |
            (((uint32_t)p[3]) << 24));
}

// Returns true if header is successfully parsed.
bool
load_bmp_file_header(BmpFileHeaderOnDisk ondisk, BmpFileHeader *inmem)
{
    if (ondisk[BMP_FILE_HEADER_MAGIC_1] != 'B' ||
        ondisk[BMP_FILE_HEADER_MAGIC_2] != 'M' ||
        le32_to_cpu(ondisk + BMP_FILE_HEADER_RESERVED) != 0)
        return false; // not a BMP file

    inmem->filesize = le32_to_cpu(ondisk + BMP_FILE_HEADER_FILESIZE);
    inmem->headerOffset = le32_to_cpu(ondisk + BMP_FILE_HEADER_HDROFF);
    return true;
}
Run Code Online (Sandbox Code Playgroud)

相反的方式,以及"信息"标题的处理,留下练习.

  • 像这样的代码是*正确的*(假设我没有犯错,无论如何),并且试图"通过结构来做"的代码是*不正确*; 资源消耗没有实际意义. (2认同)
  • 反过来说,你编写一个函数`store_bmp_file_header`,它从`BmpFileHeader`转换回`BmpFileHeaderOnDisk`,然后你把`fwrite`应用到'BmpFileHeaderOnDisk`,`size = 1,nmemb = BMP_FILE_HEADER_SIZE`.调用`fwrite`并将其`size`参数设置为1并将其`nmemb`参数设置为您要写入的任何内容的字节大小始终是最好的 - 否则您无法从短写入中恢复. (2认同)