取消字节对齐

还是字节对齐.
题外话: 前些日子在烤烟机项目中 写 数据公共模块的时候曾经出现过 : 相同的结构体代码, 在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)是位对齐。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值