#include <vcl.h>
#pragma hdrstop
#include<stdio.h>
#include "Unit1.h"
#include"File1.h"
struct BITMAPFILEHEADER_
{
short type;
int bfSize;
short re1,re2;
int Offbits;
};
struct BITMAPINFO_
{
long size;
long width,height;
short planes,bitCount;
long comp,sizeImg;
long xpels,ypels;
long used,important;
};
struct COLORTABLE_
{
char blue,green,red,res;
};
void xxx()
{
FILE *f=fopen("F://projects//bmp//1.bmp","rb");
if(f==NULL) /*判断文件是否打开成功*/
{
ShowMessage("File open error");
return;
}
fseek(f,0,0);
BITMAPFILEHEADER_ *bmph=new BITMAPFILEHEADER_();
if(fgets((char*)bmph,sizeof(BITMAPFILEHEADER_),f)==NULL)
{
ShowMessage("File read error");
return;
}
BITMAPINFO_ *bmpi=new BITMAPINFO_();
if(fgets((char*)bmpi,sizeof(BITMAPINFO_),f)==NULL)
{
ShowMessage("File read error2");
return;
}
Form1->Edit1->Text=IntToStr(bmph->bfSize);
Form1->Edit2->Text=IntToStr(bmpi->width);
Form1->Edit3->Text=IntToStr(bmpi->height);
fclose(f);
};
这是一个用BCB写的普通的读取BMP文件头信息的简单程序,但工作不正常。
显示结果
Edit1:0
Edit2:1073741824
Edit3:16777216
苦思冥想解释不了,用Ultra edit打开BMP文件发现文件开头是
42 4D 38 14 00 00 00 00 00 00 36 04 00 00 28 00
文件是没错的,42 4D是BMP文件标志“BM”,接下来38 14 00 00 是文件大小,INT值是0X1438=5176字节,和WINDOWS显示的一样,没错。
后来写了个程序读出第一个自己‘B’和第2个字节‘M’却时正确的,就时到读size时会变成0,只能怀疑是BITMAPFILEHEADER_结构匹配数据时出了问题,为了进一步验证,写了另一个程序自己填充BMP文件头,写入磁盘,再用Ultra edit查看,程序代码是这样的。
#pragma argsused
#include<stdio.h>
#include<stdlib.h>
struct BITMAPFILEHEADER_
{
short type;
int bfSize;
short re1,re2;
int Offbits;
};
int main(int argc, char* argv[])
{
FILE *f=fopen( "f://abc.txt","wb");
BITMAPFILEHEADER_ *b=new BITMAPFILEHEADER_();
b->type=1;
b->bfSize=1;
b->re1=b->re2=1;
b->Offbits=1;
if(f==NULL)
{
printf("xxx");
system("pause");
return 0;
}
fwrite( b,sizeof(BITMAPFILEHEADER_),1,f);
system("pause");
return 0;
}
用Ultra Edit 查看发现居然写了 16 个字节,如下
01 00 67 32 01 00 00 00 01 00 01 00 01 00 00 00
写了 type 后无缘无故多了个 67 32,后面的又没问题,突然想起论坛上有讨论过C语言的字节对齐问题,就怀疑是字节对齐的问题,到网上找BCB字节对齐的指令,原来BCB默认是4字节对齐,用#pragma pack(1)可以把字节对齐设置到1字节。
加上#pragma pack(1),读出的数据就正确了。
奔腾机器是32位系统,数据总线宽度32位,CPU一次从内存传送4字节的数据,为了取一个数据时能把访问存储器的减到最少,编译器默认结构类型的数据以4字节对齐,这样读取上面那个BITMAPFILEHEADER_结构需要4次访问内存(16字节每次读4字节),跟在这个结构后面的数据也在内存中对齐了,如果后面再跟着一个BITMAPFILEHEADER_结构,那么读取后面那个BITMAPFILEHEADER_也只需要4次访问内存,如果用#pragma pack(1)强制1字节对齐后,访问第一个BITMAPFILEHEADER_时仍然时4次访问内存,但访问后面紧接着的那个BITMAPFILEHEADER_时就需要5次访问内存,而且再后面接着的数据也对不齐了。
虽然出于性能的考虑编译器默认4字节对齐,但有时用户用户有有自己的需要不对齐,就像上面那种情况,这时就要用IDE指令#pragma pack(1)强制不对齐了。