#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)強制不對齊了。