还是字节对齐.
题外话: 前些日子在烤烟机项目中 写 数据公共模块的时候曾经出现过 : 相同的结构体代码, 在windows 下编译比在linux下编译小. 当时也考虑到是内存对齐的问题, 于是用#pragma pack(N)的方式来强制使其大小一致. 但是结果不对.
知道今天处理BMP图像时候, 才发现问题在这里.
在linux中用__attribute__ ((packed)) ,在VC中:在需要紧凑对齐的代码段前后用#pragma pack(1).
现将其文章贴出:
结构体内的成员有时候会为了访问速度的原因,进行一些对齐,像前面的一篇文章写的那样,比如含有一个字节的char和一个整数int的结构体,大小是8,而不是5.有时候,会不需要这种对齐,比如写一个Bitmap文件的头:这些头信息可以封装在两个结构体中:BITMAPFILEHEADER,BITMAPINFOHEADER,然后才是BMP数据。而这两个结构体中就的防止字节对齐的现象出现,我就犯过这样的错误。在linux中用__attribute__ ((packed)) ,在VC中:在需要紧凑对齐的代码段前后用#pragma pack(1), #pragma pack()包含,恩,贴上我的代码。
typedef struct tagBITMAPINFOHEADER
{
UINT32 biSize;
long biWidth;
long biHeight;
UINT16 biPlanes;
UINT16 biBitCount;
UINT32 biCompression;
UINT32 biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
UINT32 biClrUsed;
UINT32 biClrImportant;
}__attribute__ ((packed)) BITMAPINFOHEADER;
typedef struct tagBITMAPFILEHEADER
{
UINT16 bfType;
UINT32 bfSize;
UINT16 bfReserved1;
UINT16 bfReserved2;
UINT32 bfOffBits;
}__attribute__ ((packed)) BITMAPFILEHEADER;
int SaveBitmap(const char *data, const char *filepath, int width, int height)
{
FILE *file;
int length = width*height*3;
file = fopen(filepath, "w+b");
if (file == NULL)
{
printf("error fopen\n");
fclose(file);
return -1;
}
else
{
BITMAPFILEHEADER bfh;
memset( &bfh, 0, sizeof( bfh ) );
bfh.bfType = 'MB';
bfh.bfSize = sizeof( bfh ) + length + sizeof( BITMAPINFOHEADER );
bfh.bfOffBits = sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER );
fwrite(&bfh,sizeof(bfh),1, file);
BITMAPINFOHEADER bih;
memset( &bih, 0, sizeof( bih ) );
bih.biSize = sizeof( bih );
bih.biWidth = width;
bih.biHeight = height;
bih.biPlanes = 1;
bih.biBitCount = 24;
fwrite(&bih, sizeof(bih), 1, file);
fwrite(data,length, 1, file);
fclose(file);
return 0;
}
}
----------------------------------------------------------------------------------------------------------------------
附贴另一篇文章
1. BITMAPFILEHEADER.bfOffBits到底应该填什么数字呢?我打开了两幅灰度图,发现都是2090008576。 但是教科书及网上众多的指南上都说的是bfOffBits是从文件头到实际DIB数据的偏移量。 对24-bit图像,我把bfOffBits设为54,用windows的预览功能就打不开;但是变成2090008576就打开了,这是怎么回事? 2. 在写打开BMP图像文件时我碰到如下问题: 在Dev-C++ 4.9.9.2中,我自己定义的BITMAPFILEHEADER,sizeof(BITMAPFILEHEADER)大小是16bytes;在VC 6.0中,定义同样的结构也是16bytes。 但是在VC 6.0 中用VC自己定义的BITMAPFILEHDAER时是14bytes。 这是为什么呢? 在Dev-C++中BITMAPFILEHEADER是这样定义的(和VC中的定义一样) struct BITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} ; 其中WORD,DWORD的定义也是和VC相同 网上一些解决方法: 解决方案:见http://community.csdn.net/Expert/topic/4333/4333278.xml?temp=.474209 或http://tech.cixiong.com/t/200510/18/0861127.html 这是一个字节对齐的问题,详见ostinmymind的blog :http://lostinmymind.blogchina.com/,这篇文章我也收进了我的blog,名为“sizeof的问题(字节对齐等)” 以前写的错误的没有考虑到对齐的完整的程序:
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <cstdlib>
using namespace std;
typedef unsigned long DWORD;
typedef unsigned short WORD;
//#pragma pack(1)
struct BITMAPINFOHEADER{
DWORD biSize;
long biWidth;
long biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
typedef struct BITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
};
//#pragma pack()
int main(int argc, char * argv[])
{
if (argc<=1){
cout<<"No file name input!"<<endl;
return (-1);
}
cout<<"the file name is "<<argv[1]<<endl;
ifstream bmp(argv[1], ios_base::binary);
if (!bmp){
cout<<"cannot open file!"<<endl;
return -1;
}
DWORD filesize;
bmp.seekg(0, ios_base::end);
streampos pos=bmp.tellg();
filesize=(DWORD)pos;
cout<<"file size "<<filesize<<endl;
// consider the specific characteristics of bmp files
BITMAPFILEHEADER m_BFH;
BITMAPINFOHEADER m_BIH;
const int BFHSize=sizeof(BITMAPFILEHEADER)-2;
const int BIHSize=sizeof(BITMAPINFOHEADER);
bmp.seekg(0);
bmp.read((char *)&m_BFH, BFHSize);
cout<<"sizeof(BITMAPFILEHEADER) "<<BFHSize<<endl;
cout<<"sizeof(BITMAPINFOHEADER) "<<BIHSize<<endl;
cout<<"bfType whether a bmp file "<<m_BFH.bfType<<endl;
cout<<"bfSize: File Size "<<m_BFH.bfSize<<endl;
cout<<"bfReserved1= "<<m_BFH.bfReserved1<<endl;
cout<<"bfReserved2= "<<m_BFH.bfReserved2<<endl;
cout<<"bfOffBits= "<<m_BFH.bfOffBits<<endl;
bmp.seekg(BFHSize);
bmp.read((char *)&m_BIH,BIHSize);
cout<<"BITMAPINFORHEADER size "<<m_BIH.biSize<<endl;
cout<<"BITMAP width "<<m_BIH.biWidth<<endl;
cout<<"BITMAP height "<<m_BIH.biHeight<<endl;
cout<<"biPlanes "<<m_BIH.biPlanes<<endl;
cout<<"biBitCount:bits/pixel "<<m_BIH.biBitCount<<endl;
cout<<"biCompression: compression type "<<m_BIH.biCompression<<endl;
cout<<"biSizeImage: Image Size "<<m_BIH.biSizeImage<<endl;
cout<<"biXPelsPerMeter: HorizontalResolution "<<m_BIH.biXPelsPerMeter<<endl;
cout<<"biXPelsPerMeter: VerticalResolution "<<m_BIH.biYPelsPerMeter<<endl;
cout<<"biClrUsed "<<m_BIH.biClrUsed<<endl;
cout<<"biClrImportant "<<m_BIH.biClrImportant<<endl;
return 0;
}
我的一些经验和体会 事实证明上述问题都是字节对齐而引起,加上#pragma pack(1) #pragma pack()以后就是正确的了, 24-bit图像的bfOffBits是54。
#pragma pack(1)
struct BITMAPINFOHEADER{
DWORD biSize;
long biWidth;
long biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
struct BITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
};
#pragma pack()
所以以后自己定义struct时一定要加上上述预编译语句,当然也可以在编译器里设置。 使用#pragma容易影响其他部分的工作,还有一种方法是只对单个定义来设置对齐方式: typedef struct {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
}__attribute__((packed)) BITMAPFILEHEADER; 体会:刚才跟师兄讨论了一下这个问题,得到了一些启发。在以后写程序时,一定要有字节对齐的意识,以免出错。这种因为对齐而出现的读取错误一般出现在由磁盘向内存写struct的时候,在内存中读写的时候一般不会出现这种问题。 其实像上面那样强制进行对齐方式的变化,是不好的。比较好的方式是,不写强制对齐的语句,而是按字节读取,按所应取的值在文件中的位置用fseek和fread来取。下面是我写的一个简化了的读BMP文件头的程序(c和 c++混杂,耐心看哈。。。):
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <cstdlib>
using namespace std;
typedef unsigned long DWORD;
typedef unsigned short WORD;
struct BITMAPINFOHEADER{
DWORD biSize;
long biWidth;
long biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
struct BITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
};
int main(int argc, char * argv[])
{
if (argc!=2){
cout<<"usage: There should be one and only one parameter!"<<endl;
cout<<" And the parameter is the name of image!"<<endl;
return (-1);
}
cout<<"the file name is "<<argv[1]<<endl;
ifstream bmp(argv[1], ios_base::binary);
if (!bmp){
cout<<"cannot open file!"<<endl;
return -1;
}
DWORD filesize;
bmp.seekg(0, ios_base::end);
streampos pos=bmp.tellg();
filesize=(DWORD)pos;
cout<<"file size "<<filesize<<endl;
// consider the specific characteristics of bmp files
BITMAPFILEHEADER m_BFH;
BITMAPINFOHEADER m_BIH;
const int BFHSize=sizeof(BITMAPFILEHEADER);
const int BIHSize=sizeof(BITMAPINFOHEADER);
char buf[BFHSize];
bmp.seekg(0);
bmp.read(buf, BFHSize);
memcpy(&(m_BFH.bfType),buf, sizeof(WORD));
memcpy(&(m_BFH.bfSize),buf+sizeof(WORD), sizeof(DWORD));
memcpy(&(m_BFH.bfReserved1),buf+sizeof(WORD)+sizeof(DWORD),sizeof(WORD));
memcpy(&(m_BFH.bfReserved2),buf+2*sizeof(WORD)+sizeof(DWORD),sizeof(WORD));
memcpy(&(m_BFH.bfOffBits),buf+3*sizeof(WORD)+sizeof(DWORD),sizeof(DWORD));
cout<<"sizeof(DWORD)"<<sizeof(DWORD)<<endl;
cout<<"sizeof(WORD)"<<sizeof(WORD)<<endl;
cout<<"sizeof(BITMAPFILEHEADER) "<<BFHSize<<endl;
cout<<"sizeof(BITMAPINFOHEADER) "<<BIHSize<<endl;
cout<<"bfType whether a bmp file "<<m_BFH.bfType<<endl;
cout<<"bfSize: File Size "<<m_BFH.bfSize<<endl;
cout<<"bfReserved1= "<<m_BFH.bfReserved1<<endl;
cout<<"bfReserved2= "<<m_BFH.bfReserved2<<endl;
cout<<"bfOffBits= "<<m_BFH.bfOffBits<<endl;
// since BIHSize is 40, function read can deal with it well
bmp.seekg(BFHSize-2);
bmp.read((char *)&m_BIH,BIHSize);
cout<<"BITMAPINFORHEADER size "<<m_BIH.biSize<<endl;
cout<<"BITMAP width "<<m_BIH.biWidth<<endl;
cout<<"BITMAP height "<<m_BIH.biHeight<<endl;
cout<<"biPlanes "<<m_BIH.biPlanes<<endl;
cout<<"biBitCount:bits/pixel "<<m_BIH.biBitCount<<endl;
cout<<"biCompression: compression type "<<m_BIH.biCompression<<endl;
cout<<"biSizeImage: Image Size "<<m_BIH.biSizeImage<<endl;
cout<<"biXPelsPerMeter: HorizontalResolution "<<m_BIH.biXPelsPerMeter<<endl;
cout<<"biXPelsPerMeter: VerticalResolution "<<m_BIH.biYPelsPerMeter<<endl;
cout<<"biClrUsed "<<m_BIH.biClrUsed<<endl;
cout<<"biClrImportant "<<m_BIH.biClrImportant<<endl;
return 0;
}
-------------------------------------------------------------------------------------------------------------------
__attrubte__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
#define __u8 unsigned char
#define __u16 unsigned short
/* __attribute__ ((packed)) 的位置约束是放于声明的尾部“;”之前 */
struct str_struct{
__u8 a;
__u8 b;
__u8 c;
__u16 d;
} __attribute__ ((packed));
/* 当用到typedef时,要特别注意__attribute__ ((packed))放置的位置,相当于:
* typedef struct str_stuct str;
* 而struct str_struct 就是上面的那个结构。
*/
typedef struct {
__u8 a;
__u8 b;
__u8 c;
__u16 d;
} __attribute__ ((packed)) str;
/* 在下面这个typedef结构中,__attribute__ ((packed))放在结构名str_temp之后,其作用是被忽略的,注意与结构str的区别。*/
typedef struct {
__u8 a;
__u8 b;
__u8 c;
__u16 d;
}str_temp __attribute__ ((packed));
typedef struct {
__u8 a;
__u8 b;
__u8 c;
__u16 d;
}str_nopacked;
int main(void)
{
printf("sizeof str_struct = %d\n", sizeof(struct str_struct));
printf("sizeof str = %d\n", sizeof(str));
printf("sizeof str_temp = %d\n", sizeof(str_temp));
printf("sizeof str_nopacked = %d\n", sizeof(str_nopacked));
return 0;
}
编译运行:
[root@localhost root]# ./packedtest
sizeof str_struct = 5
sizeof str = 5
sizeof str_temp = 6
sizeof str_nopacked = 6
--------------------------------------------------------------------
GNU C的一大特色就是__attribute__机制。__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__书写特征是:__attribute__前后都有两个下划线,并且后面会紧跟一对括弧,括弧里面是相应的__attribute__参数。
__attribute__语法格式为:
__attribute__ ((attribute-list))
其位置约束:放于声明的尾部“;”之前。
函数属性(Function Attribute):函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。__attribute__机制也很容易同非GNU应用程序做到兼容之功效。
GNU CC需要使用 –Wall编译器来击活该功能,这是控制警告信息的一个很好的方式。
packed属性:使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。
题外话: 前些日子在烤烟机项目中 写 数据公共模块的时候曾经出现过 : 相同的结构体代码, 在windows 下编译比在linux下编译小. 当时也考虑到是内存对齐的问题, 于是用#pragma pack(N)的方式来强制使其大小一致. 但是结果不对.
知道今天处理BMP图像时候, 才发现问题在这里.
在linux中用__attribute__ ((packed)) ,在VC中:在需要紧凑对齐的代码段前后用#pragma pack(1).
现将其文章贴出:
结构体内的成员有时候会为了访问速度的原因,进行一些对齐,像前面的一篇文章写的那样,比如含有一个字节的char和一个整数int的结构体,大小是8,而不是5.有时候,会不需要这种对齐,比如写一个Bitmap文件的头:这些头信息可以封装在两个结构体中:BITMAPFILEHEADER,BITMAPINFOHEADER,然后才是BMP数据。而这两个结构体中就的防止字节对齐的现象出现,我就犯过这样的错误。在linux中用__attribute__ ((packed)) ,在VC中:在需要紧凑对齐的代码段前后用#pragma pack(1), #pragma pack()包含,恩,贴上我的代码。
typedef struct tagBITMAPINFOHEADER
{
UINT32 biSize;
long biWidth;
long biHeight;
UINT16 biPlanes;
UINT16 biBitCount;
UINT32 biCompression;
UINT32 biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
UINT32 biClrUsed;
UINT32 biClrImportant;
}__attribute__ ((packed)) BITMAPINFOHEADER;
typedef struct tagBITMAPFILEHEADER
{
UINT16 bfType;
UINT32 bfSize;
UINT16 bfReserved1;
UINT16 bfReserved2;
UINT32 bfOffBits;
}__attribute__ ((packed)) BITMAPFILEHEADER;
int SaveBitmap(const char *data, const char *filepath, int width, int height)
{
FILE *file;
int length = width*height*3;
file = fopen(filepath, "w+b");
if (file == NULL)
{
printf("error fopen\n");
fclose(file);
return -1;
}
else
{
BITMAPFILEHEADER bfh;
memset( &bfh, 0, sizeof( bfh ) );
bfh.bfType = 'MB';
bfh.bfSize = sizeof( bfh ) + length + sizeof( BITMAPINFOHEADER );
bfh.bfOffBits = sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER );
fwrite(&bfh,sizeof(bfh),1, file);
BITMAPINFOHEADER bih;
memset( &bih, 0, sizeof( bih ) );
bih.biSize = sizeof( bih );
bih.biWidth = width;
bih.biHeight = height;
bih.biPlanes = 1;
bih.biBitCount = 24;
fwrite(&bih, sizeof(bih), 1, file);
fwrite(data,length, 1, file);
fclose(file);
return 0;
}
}
----------------------------------------------------------------------------------------------------------------------
附贴另一篇文章
1. BITMAPFILEHEADER.bfOffBits到底应该填什么数字呢?我打开了两幅灰度图,发现都是2090008576。 但是教科书及网上众多的指南上都说的是bfOffBits是从文件头到实际DIB数据的偏移量。 对24-bit图像,我把bfOffBits设为54,用windows的预览功能就打不开;但是变成2090008576就打开了,这是怎么回事? 2. 在写打开BMP图像文件时我碰到如下问题: 在Dev-C++ 4.9.9.2中,我自己定义的BITMAPFILEHEADER,sizeof(BITMAPFILEHEADER)大小是16bytes;在VC 6.0中,定义同样的结构也是16bytes。 但是在VC 6.0 中用VC自己定义的BITMAPFILEHDAER时是14bytes。 这是为什么呢? 在Dev-C++中BITMAPFILEHEADER是这样定义的(和VC中的定义一样) struct BITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} ; 其中WORD,DWORD的定义也是和VC相同 网上一些解决方法: 解决方案:见http://community.csdn.net/Expert/topic/4333/4333278.xml?temp=.474209 或http://tech.cixiong.com/t/200510/18/0861127.html 这是一个字节对齐的问题,详见ostinmymind的blog :http://lostinmymind.blogchina.com/,这篇文章我也收进了我的blog,名为“sizeof的问题(字节对齐等)” 以前写的错误的没有考虑到对齐的完整的程序:
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <cstdlib>
using namespace std;
typedef unsigned long DWORD;
typedef unsigned short WORD;
//#pragma pack(1)
struct BITMAPINFOHEADER{
DWORD biSize;
long biWidth;
long biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
typedef struct BITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
};
//#pragma pack()
int main(int argc, char * argv[])
{
if (argc<=1){
cout<<"No file name input!"<<endl;
return (-1);
}
cout<<"the file name is "<<argv[1]<<endl;
ifstream bmp(argv[1], ios_base::binary);
if (!bmp){
cout<<"cannot open file!"<<endl;
return -1;
}
DWORD filesize;
bmp.seekg(0, ios_base::end);
streampos pos=bmp.tellg();
filesize=(DWORD)pos;
cout<<"file size "<<filesize<<endl;
// consider the specific characteristics of bmp files
BITMAPFILEHEADER m_BFH;
BITMAPINFOHEADER m_BIH;
const int BFHSize=sizeof(BITMAPFILEHEADER)-2;
const int BIHSize=sizeof(BITMAPINFOHEADER);
bmp.seekg(0);
bmp.read((char *)&m_BFH, BFHSize);
cout<<"sizeof(BITMAPFILEHEADER) "<<BFHSize<<endl;
cout<<"sizeof(BITMAPINFOHEADER) "<<BIHSize<<endl;
cout<<"bfType whether a bmp file "<<m_BFH.bfType<<endl;
cout<<"bfSize: File Size "<<m_BFH.bfSize<<endl;
cout<<"bfReserved1= "<<m_BFH.bfReserved1<<endl;
cout<<"bfReserved2= "<<m_BFH.bfReserved2<<endl;
cout<<"bfOffBits= "<<m_BFH.bfOffBits<<endl;
bmp.seekg(BFHSize);
bmp.read((char *)&m_BIH,BIHSize);
cout<<"BITMAPINFORHEADER size "<<m_BIH.biSize<<endl;
cout<<"BITMAP width "<<m_BIH.biWidth<<endl;
cout<<"BITMAP height "<<m_BIH.biHeight<<endl;
cout<<"biPlanes "<<m_BIH.biPlanes<<endl;
cout<<"biBitCount:bits/pixel "<<m_BIH.biBitCount<<endl;
cout<<"biCompression: compression type "<<m_BIH.biCompression<<endl;
cout<<"biSizeImage: Image Size "<<m_BIH.biSizeImage<<endl;
cout<<"biXPelsPerMeter: HorizontalResolution "<<m_BIH.biXPelsPerMeter<<endl;
cout<<"biXPelsPerMeter: VerticalResolution "<<m_BIH.biYPelsPerMeter<<endl;
cout<<"biClrUsed "<<m_BIH.biClrUsed<<endl;
cout<<"biClrImportant "<<m_BIH.biClrImportant<<endl;
return 0;
}
我的一些经验和体会 事实证明上述问题都是字节对齐而引起,加上#pragma pack(1) #pragma pack()以后就是正确的了, 24-bit图像的bfOffBits是54。
#pragma pack(1)
struct BITMAPINFOHEADER{
DWORD biSize;
long biWidth;
long biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
struct BITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
};
#pragma pack()
所以以后自己定义struct时一定要加上上述预编译语句,当然也可以在编译器里设置。 使用#pragma容易影响其他部分的工作,还有一种方法是只对单个定义来设置对齐方式: typedef struct {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
}__attribute__((packed)) BITMAPFILEHEADER; 体会:刚才跟师兄讨论了一下这个问题,得到了一些启发。在以后写程序时,一定要有字节对齐的意识,以免出错。这种因为对齐而出现的读取错误一般出现在由磁盘向内存写struct的时候,在内存中读写的时候一般不会出现这种问题。 其实像上面那样强制进行对齐方式的变化,是不好的。比较好的方式是,不写强制对齐的语句,而是按字节读取,按所应取的值在文件中的位置用fseek和fread来取。下面是我写的一个简化了的读BMP文件头的程序(c和 c++混杂,耐心看哈。。。):
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <cstdlib>
using namespace std;
typedef unsigned long DWORD;
typedef unsigned short WORD;
struct BITMAPINFOHEADER{
DWORD biSize;
long biWidth;
long biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};
struct BITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
};
int main(int argc, char * argv[])
{
if (argc!=2){
cout<<"usage: There should be one and only one parameter!"<<endl;
cout<<" And the parameter is the name of image!"<<endl;
return (-1);
}
cout<<"the file name is "<<argv[1]<<endl;
ifstream bmp(argv[1], ios_base::binary);
if (!bmp){
cout<<"cannot open file!"<<endl;
return -1;
}
DWORD filesize;
bmp.seekg(0, ios_base::end);
streampos pos=bmp.tellg();
filesize=(DWORD)pos;
cout<<"file size "<<filesize<<endl;
// consider the specific characteristics of bmp files
BITMAPFILEHEADER m_BFH;
BITMAPINFOHEADER m_BIH;
const int BFHSize=sizeof(BITMAPFILEHEADER);
const int BIHSize=sizeof(BITMAPINFOHEADER);
char buf[BFHSize];
bmp.seekg(0);
bmp.read(buf, BFHSize);
memcpy(&(m_BFH.bfType),buf, sizeof(WORD));
memcpy(&(m_BFH.bfSize),buf+sizeof(WORD), sizeof(DWORD));
memcpy(&(m_BFH.bfReserved1),buf+sizeof(WORD)+sizeof(DWORD),sizeof(WORD));
memcpy(&(m_BFH.bfReserved2),buf+2*sizeof(WORD)+sizeof(DWORD),sizeof(WORD));
memcpy(&(m_BFH.bfOffBits),buf+3*sizeof(WORD)+sizeof(DWORD),sizeof(DWORD));
cout<<"sizeof(DWORD)"<<sizeof(DWORD)<<endl;
cout<<"sizeof(WORD)"<<sizeof(WORD)<<endl;
cout<<"sizeof(BITMAPFILEHEADER) "<<BFHSize<<endl;
cout<<"sizeof(BITMAPINFOHEADER) "<<BIHSize<<endl;
cout<<"bfType whether a bmp file "<<m_BFH.bfType<<endl;
cout<<"bfSize: File Size "<<m_BFH.bfSize<<endl;
cout<<"bfReserved1= "<<m_BFH.bfReserved1<<endl;
cout<<"bfReserved2= "<<m_BFH.bfReserved2<<endl;
cout<<"bfOffBits= "<<m_BFH.bfOffBits<<endl;
// since BIHSize is 40, function read can deal with it well
bmp.seekg(BFHSize-2);
bmp.read((char *)&m_BIH,BIHSize);
cout<<"BITMAPINFORHEADER size "<<m_BIH.biSize<<endl;
cout<<"BITMAP width "<<m_BIH.biWidth<<endl;
cout<<"BITMAP height "<<m_BIH.biHeight<<endl;
cout<<"biPlanes "<<m_BIH.biPlanes<<endl;
cout<<"biBitCount:bits/pixel "<<m_BIH.biBitCount<<endl;
cout<<"biCompression: compression type "<<m_BIH.biCompression<<endl;
cout<<"biSizeImage: Image Size "<<m_BIH.biSizeImage<<endl;
cout<<"biXPelsPerMeter: HorizontalResolution "<<m_BIH.biXPelsPerMeter<<endl;
cout<<"biXPelsPerMeter: VerticalResolution "<<m_BIH.biYPelsPerMeter<<endl;
cout<<"biClrUsed "<<m_BIH.biClrUsed<<endl;
cout<<"biClrImportant "<<m_BIH.biClrImportant<<endl;
return 0;
}
-------------------------------------------------------------------------------------------------------------------
__attrubte__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
#define __u8 unsigned char
#define __u16 unsigned short
/* __attribute__ ((packed)) 的位置约束是放于声明的尾部“;”之前 */
struct str_struct{
__u8 a;
__u8 b;
__u8 c;
__u16 d;
} __attribute__ ((packed));
/* 当用到typedef时,要特别注意__attribute__ ((packed))放置的位置,相当于:
* typedef struct str_stuct str;
* 而struct str_struct 就是上面的那个结构。
*/
typedef struct {
__u8 a;
__u8 b;
__u8 c;
__u16 d;
} __attribute__ ((packed)) str;
/* 在下面这个typedef结构中,__attribute__ ((packed))放在结构名str_temp之后,其作用是被忽略的,注意与结构str的区别。*/
typedef struct {
__u8 a;
__u8 b;
__u8 c;
__u16 d;
}str_temp __attribute__ ((packed));
typedef struct {
__u8 a;
__u8 b;
__u8 c;
__u16 d;
}str_nopacked;
int main(void)
{
printf("sizeof str_struct = %d\n", sizeof(struct str_struct));
printf("sizeof str = %d\n", sizeof(str));
printf("sizeof str_temp = %d\n", sizeof(str_temp));
printf("sizeof str_nopacked = %d\n", sizeof(str_nopacked));
return 0;
}
编译运行:
[root@localhost root]# ./packedtest
sizeof str_struct = 5
sizeof str = 5
sizeof str_temp = 6
sizeof str_nopacked = 6
--------------------------------------------------------------------
GNU C的一大特色就是__attribute__机制。__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
__attribute__书写特征是:__attribute__前后都有两个下划线,并且后面会紧跟一对括弧,括弧里面是相应的__attribute__参数。
__attribute__语法格式为:
__attribute__ ((attribute-list))
其位置约束:放于声明的尾部“;”之前。
函数属性(Function Attribute):函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。__attribute__机制也很容易同非GNU应用程序做到兼容之功效。
GNU CC需要使用 –Wall编译器来击活该功能,这是控制警告信息的一个很好的方式。
packed属性:使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。