关于结构体字对齐—BMP文件图像处理程序

最近用C写了个关于BMP文件图像处理的简单程序,结果算法写了不用1个小时搞定,而次要的文件输入输出搞了我半天才搞定。

主要问题有两点:

第一,在读取BMP文件的文件头信息放入结构体时,发现中间会漏了2个字节,原因是结构体字对齐,结果导致整体的错位,一个输入66K的图片,输出有178MB。

解决办法:不将结构体整个一起read、write,而是将其中的每个成员一个一个地read、write。当然,牺牲了效率。

 

第二,之前以为BMP灰度图的颜色表信息是没用的,后来查了资料才发现,只有24位深的BMP图才没有颜色表项,对于其他的1、4、8位深还是有用的。

解决办法:将颜色表项读入内存,然后再写入新图像文件中。对于8bit的BMP灰度图,颜色表就是0 0 0 0 ~ 255 255 255 0的灰度值

/*********************************以下是关于BMP文件图像处理程序源代码***************************/

//*********************image.h****************

#ifndef IMAGE_H
#define IMAGE_H
void image_info(FILE* file);
void image_save(FILE *file);
void image_smooth(); //图像平滑
void image_Roberts_sharpen(); //Roberts算法的图像锐化
void image_Roberts_enhance(); //Roberts算法的图像增强
void image_median_filter(); //中值滤波
typedef struct BMP //其实BMP文件头、位图信息头、颜色信息的结构体在<windows.h>头文件中有
{
unsigned short bfType; //文件标识 2字节 必须为BM
unsigned int bfSize; //文件大小 4字节
unsigned short bfReserved1; //保留,每字节以"00"填写 2字节
unsigned short bfReserved2; //同上 2字节
unsigned int bfOffBits; //记录图像数据区的起始位置(图象数据相对于文件头字节的偏移量)。 4字节

//40字节
unsigned int biSize; //表示本结构的大小 4字节
int biWidth; //位图的宽度 4字节
int biHeight; //位图的高度 4字节
unsigned short biPlanes; //永远为1 , 2字节
unsigned short biBitCount; //位图的位数 分为1 4 8 16 24 32 2字节
unsigned int biCompression; //压缩说明 4字节
unsigned int biSizeImage; //表示位图数据区域的大小以字节为单位 4字节
int biXPelsPerMeter; //用象素/米表示的水平分辨率 4字节
int biYPelsPerMeter; //用象素/米表示的垂直分辨率 4字节
unsigned int biClrUsed; //位图使用的颜色索引数 4字节
unsigned int biClrImportant; //对图象显示有重要影响的颜色索引的数目 4字节

} BMP;

 

int color_table_length; //颜色表项数
int line_byte;
unsigned char *imagedata;
unsigned char *temp;
extern BMP bmp;
unsigned char *pColorTable;//颜色表指针
extern unsigned char *pColorTable;//颜色表指针
extern int line_byte;
extern int color_table_length;
extern unsigned char *imagedata;
extern unsigned char *temp;
#endif

//****************image_rw***************

#include<stdio.h>
#include<stdlib.h>
//#include<windows.h>
#include"image.h"
void image_info(FILE *file)
{


//int times=3; //输入文件名次数。
char bmp_name[10]; //文件名

printf("\nplease enter a file name for reading:");
do
{
fflush(stdin);
gets(bmp_name);
file=fopen(bmp_name,"rb+"); //打开一个文件进行读写操作。
if (file==NULL)
{
printf("\nerror opening %s for reading! ",bmp_name);
}
else
{
break;
}
}
while(1);


//读取图像信息

fseek(file,0L,0); //读取图像文件类型
//fread(&bmp,sizeof(struct BMP),1,file);
fseek(file,0L,0); //图像文件类型
fread(&(bmp.bfType),sizeof(short),1,file);

fseek(file,2L,0); //图像文件大小
fread(&(bmp.bfSize),sizeof(int),1,file);

fseek(file,6L,0); //图像文件保留字1
fread(&(bmp.bfReserved1),sizeof(short),1,file);

fseek(file,8L,0); //图像文件保留字2
fread(&(bmp.bfReserved2),sizeof(short),1,file);

fseek(file,10L,0);//数据区的偏移量
fread(&(bmp.bfOffBits),sizeof(short),1,file);


fseek(file,14L,0);//文件头结构大小
fread(&(bmp.biSize),sizeof(int),1,file);


fseek(file,18L,0);//图像的宽度
fread(&(bmp.biWidth),sizeof(int),1,file);

fseek(file,22L,0);//图像的高度
fread(&(bmp.biHeight),sizeof(int),1,file);


fseek(file,26L,0);//图像的面数
fread(&(bmp.biPlanes),sizeof(short),1,file);

fseek(file,28L,0);//图像一个像素的字节数
fread(&(bmp.biBitCount),sizeof(short),1,file);

fseek(file,30L,0);//图像压缩信息
fread(&(bmp.biCompression),sizeof(short),1,file);

fseek(file,34L,0);//图像数据区的大小
fread(&(bmp.biSizeImage),sizeof(int),1,file);

fseek(file,38L,0);//水平分辨率
fread(&(bmp.biXPelsPerMeter),sizeof(int),1,file);

fseek(file,42L,0);//垂直分辨率
fread(&(bmp.biYPelsPerMeter),sizeof(int),1,file);

fseek(file,46L,0);//颜色索引数
fread(&(bmp.biClrUsed),sizeof(int),1,file);

fseek(file,50L,0);//重要颜色索引数
fread(&(bmp.biClrImportant),sizeof(int),1,file);

 

printf("\n bmp tpye: %x",bmp.bfType);
printf("\n bmp size: %u",bmp.bfSize);
printf("\n bmp reserved1: %u",bmp.bfReserved1);
printf("\n bmp reserved2: %u",bmp.bfReserved2);
printf("\n bmp offBits: %u",bmp.bfOffBits);

printf("\n bmp bisize: %u",bmp.biSize);
printf("\n bmp biWidth: %d",bmp.biWidth);
printf("\n bmp biHeight: %d",bmp.biHeight);
printf("\n bmp biplans: %u",bmp.biPlanes);
printf("\n bmp biBitCount: %u",bmp.biBitCount);
printf("\n bmp biCompression: %u",bmp.biCompression);
printf("\n bmp biSizeImage: %u",bmp.biSizeImage);
printf("\n bmp biXPelsPerMeter: %d",bmp.biXPelsPerMeter);
printf("\n bmp biYPelsPerMeter: %d",bmp.biYPelsPerMeter);
printf("\n bmp biClrUsed: %u",bmp.biClrUsed);
printf("\n bmp biClrImportant: %u\n",bmp.biClrImportant);

switch(bmp.biBitCount)
{
case 1:
color_table_length = 2;
break;
case 4:
color_table_length = 16;
break;
case 8:
color_table_length = 256;
break;
case 24:
color_table_length = 0;
break;
default:
color_table_length = 0;
printf("\n wrong biBitCount!");
}

line_byte=(( bmp.biWidth*bmp.biBitCount + 31) / 8);//获得图像数据每行的数据个数,Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,

imagedata=( unsigned char* )malloc( bmp.biSizeImage );
temp=( unsigned char* )malloc( bmp.biSizeImage );
pColorTable=(unsigned char*)malloc( color_table_length * 4 );
fseek(file, 54L, 0);
fread(pColorTable, sizeof(unsigned char), color_table_length * 4, file);
fseek(file, (long)bmp.bfOffBits, 0);
fread(imagedata, sizeof(unsigned char), bmp.biSizeImage, file);
fseek(file, (long)bmp.bfOffBits, 0);
fread(temp, sizeof(unsigned char), bmp.biSizeImage, file);

fclose(file);
}

//保存图像
void image_save(FILE *file)
{
//int times=3; //输入文件名次数。
char bmp_name[10]; //文件名
//int i; //记录数据区个数

printf("\nplease enter a file name for writeing:");
do
{
fflush(stdin);
gets(bmp_name);
file=fopen(bmp_name,"wb+"); //打开一个文件进行读写操作。
if (file==NULL)
{
printf("\nerror opening %s for writing",bmp_name);
}
else
{
break;
}
}
while(1);

 

//写文件头
printf("\n%s",bmp_name);
fseek(file,0L,0); //图像文件类型
fwrite(&(bmp.bfType),sizeof(short),1,file);
printf("\n bmp tpye: %x",bmp.bfType);

fseek(file,2L,0); //图像文件大小
fwrite(&(bmp.bfSize),sizeof(int),1,file);
printf("\n bmp size: %d",bmp.bfSize);

fseek(file,6L,0); //图像文件保留字1
fwrite(&(bmp.bfReserved1),sizeof(short),1,file);
printf("\n bmp reserved1: %d",bmp.bfReserved1);

fseek(file,8L,0); //图像文件保留字2
fwrite(&(bmp.bfReserved2),sizeof(short),1,file);
printf("\n bmp reserved2: %d",bmp.bfReserved2);

fseek(file,10L,0);//数据区的偏移量
fwrite(&(bmp.bfOffBits),sizeof(short),1,file);
printf("\n bmp offBits: %d",bmp.bfOffBits);


fseek(file,14L,0);//文件头结构大小
fwrite(&(bmp.biSize),sizeof(int),1,file);
printf("\n bmp bisize: %d",bmp.biSize);


fseek(file,18L,0);//图像的宽度
fwrite(&(bmp.biWidth),sizeof(int),1,file);
printf("\n bmp biWidth: %d",bmp.biWidth);

fseek(file,22L,0);//图像的高度
fwrite(&(bmp.biHeight),sizeof(int),1,file);
printf("\n bmp biHeight: %d",bmp.biHeight);


fseek(file,26L,0);//图像的面数
fwrite(&(bmp.biPlanes),sizeof(short),1,file);
printf("\n bmp biplans: %d",bmp.biPlanes);

fseek(file,28L,0);//图像一个像素的字节数
fwrite(&(bmp.biBitCount),sizeof(short),1,file);
printf("\n bmp biBitCount: %d",bmp.biBitCount);

fseek(file,30L,0);//图像压缩信息
fwrite(&(bmp.biCompression),sizeof(short),1,file);
printf("\n bmp biCompression: %d",bmp.biCompression);

fseek(file,34L,0);//图像数据区的大小
fwrite(&(bmp.biSizeImage),sizeof(int),1,file);
printf("\n bmp biSizeImage: %d",bmp.biSizeImage);

fseek(file,38L,0);//水平分辨率
fwrite(&(bmp.biXPelsPerMeter),sizeof(int),1,file);
printf("\n bmp biXPelsPerMeter: %d",bmp.biXPelsPerMeter);

fseek(file,42L,0);//垂直分辨率
fwrite(&(bmp.biYPelsPerMeter),sizeof(int),1,file);
printf("\n bmp biYPelsPerMeter: %d",bmp.biYPelsPerMeter);

fseek(file,46L,0);//颜色索引数
fwrite(&(bmp.biClrUsed),sizeof(int),1,file);
printf("\n bmp biClrUsed: %d",bmp.biClrUsed);

fseek(file,50L,0);//重要颜色索引数
fwrite(&(bmp.biClrImportant),sizeof(int),1,file);
printf("\n bmp biClrImportant: %d\n",bmp.biClrImportant);

fseek(file, 54L, 0);
fwrite(pColorTable, sizeof(unsigned char), color_table_length * 4, file);

fseek(file,(long)(bmp.bfOffBits),0);
fwrite(imagedata,sizeof(unsigned char),bmp.biSizeImage,file);

fclose(file);
}

//*****************pixprocess.c***************

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include"image.h"


//************************homework**********************
void image_smooth()
{
int i,j,tmp;
for (i=1;i<bmp.biHeight-1;i++)
{
for (j=1;j<bmp.biWidth-1;j++)
{
tmp = 0;
tmp += temp[(i-1)*bmp.biWidth + j-1];
tmp += temp[(i-1)*bmp.biWidth + j];
tmp += temp[(i-1)*bmp.biWidth + j+1];
tmp += temp[ i*bmp.biWidth + j-1];
tmp += temp[ i*bmp.biWidth + j+1];
tmp += temp[(i+1)*bmp.biWidth + j-1];
tmp += temp[(i+1)*bmp.biWidth + j];
tmp += temp[(i+1)*bmp.biWidth + j+1];
tmp = tmp/9;
imagedata[ i*bmp.biWidth + j ] =tmp;
}
}
}


void image_Roberts_sharpen()
{
int i,j,tmp;
for (i=1;i<bmp.biHeight-1;i++)
{
for (j=1;j<bmp.biWidth-1;j++)
{
tmp = 0;
tmp += abs( temp[(i+1)*bmp.biWidth + j-1] + 2*temp[(i+1)*bmp.biWidth + j] + temp[(i+1)*bmp.biWidth + j+1] - temp[(i-1)*bmp.biWidth + j-1] - 2*temp[(i-1)*bmp.biWidth + j] - temp[(i-1)*bmp.biWidth + j+1] );
tmp += abs( temp[(i-1)*bmp.biWidth + j+1] + 2*temp[ i*bmp.biWidth + j+1] + temp[(i+1)*bmp.biWidth + j+1] - temp[(i-1)*bmp.biWidth + j-1] - 2*temp[ i*bmp.biWidth + j-1] - temp[(i+1)*bmp.biWidth + j-1] );
imagedata[ i*bmp.biWidth + j ] =tmp;
}
}
}

void image_Roberts_enhance()
{
int i,j,tmp;
for (i=1;i<bmp.biHeight-1;i++)
{
for (j=1;j<bmp.biWidth-1;j++)
{
tmp = 0;
tmp += abs( temp[(i+1)*bmp.biWidth + j-1] + 2*temp[(i+1)*bmp.biWidth + j] + temp[(i+1)*bmp.biWidth + j+1] - temp[(i-1)*bmp.biWidth + j-1] - 2*temp[(i-1)*bmp.biWidth + j] - temp[(i-1)*bmp.biWidth + j+1] );
tmp += abs( temp[(i-1)*bmp.biWidth + j+1] + 2*temp[ i*bmp.biWidth + j+1] + temp[(i+1)*bmp.biWidth + j+1] - temp[(i-1)*bmp.biWidth + j-1] - 2*temp[ i*bmp.biWidth + j-1] - temp[(i+1)*bmp.biWidth + j-1] );
imagedata[ i*bmp.biWidth + j ] +=tmp;
}
}
}

void image_median_filter()
{
int i,j,k,m;
unsigned char tmp[9];
unsigned char change;
for (i=1;i<bmp.biHeight-1;i++)
{
for (j=1;j<bmp.biWidth-1;j++)
{
tmp[0] = temp[(i-1)*bmp.biWidth + j-1];
tmp[1] = temp[(i-1)*bmp.biWidth + j];
tmp[2] = temp[(i-1)*bmp.biWidth + j+1];
tmp[3] = temp[ i*bmp.biWidth + j-1];
tmp[4] = temp[ i*bmp.biWidth + j+1];
tmp[5] = temp[(i+1)*bmp.biWidth + j-1];
tmp[6] = temp[(i+1)*bmp.biWidth + j];
tmp[7] = temp[(i+1)*bmp.biWidth + j+1];
tmp[8] = temp[ i *bmp.biWidth + j];
for(k=0; k<9; k++)
{
for(m=k; m<9; m++)
{
if(tmp[k] > tmp[m])
{
change = tmp[k];
tmp[k] = tmp[m];
tmp[m] =change;
}
}
}
imagedata[ i*bmp.biWidth + j ] = tmp[4];
}
}
}

//***************main.c******************

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
//#include<windows.h>
#include"image.h"


//RGBQUADUAD *pColorTable;//颜色表指针
BMP bmp;

int main()
{
FILE *file=NULL;
int choose;
char go_on;
do
{
image_info(file); //imagedata已经分配了动态内存,但是没有释放

printf("\n 1.image_smooth");
printf("\n 2.image_Roberts_sharpen");
printf("\n 3.image_Roberts_enhance");
printf("\n 4.image_median_filter");
printf("\n 5.do nothing");
//printf("7.image_opposite");

printf("\nchoose your options:");
fflush(stdin);
scanf("%d",&choose);
switch(choose)
{
case 1:
image_smooth();
image_save(file);
case 2:
image_Roberts_sharpen();
image_save(file);
break;
case 3:
image_Roberts_enhance();
image_save(file);
break;
case 4:
image_median_filter();
image_save(file);
case 5:
image_save(file);
break;
default:
printf("\n wrong choose!");

}
free(imagedata);
free(temp);
free(pColorTable);
printf("\nlet's go on?(y/n):");
fflush(stdin);
scanf("%c",&go_on);
if (go_on=='n')
{
printf("\nbye bye!");
break;
}
}
while(1);

return 0;
}

/*********************************以下是关于BMP文件结构的内容【百度百科来的】***************************/

组成

 

  典型的BMP图像文件由四部分组成:

 

  1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;

 

  2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;

 

  3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;

 

  4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。

 

对应的数据结构

  1:BMP文件组成

 

  BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。

 

  2:BMP文件头(14字节)

 

  BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。

 

  其结构定义如下:

 

  typedef struct tagBITMAPFILEHEADER

 

  {

 

  WORD bfType; // 位图文件的类型,必须为BM(0-1字节)

 

  DWORD bfSize; // 位图文件的大小,以字节为单位(2-5字节)

 

  WORD bfReserved1; // 位图文件保留字,必须为0(6-7字节)

 

  WORD bfReserved2; // 位图文件保留字,必须为0(8-9字节)

 

  DWORD bfOffBits; // 位图数据的起始位置,以相对于位图(10-13字节)

 

  // 文件头的偏移量表示,以字节为单位

 

  } BITMAPFILEHEADER;

 

  3:位图信息头(40字节)

 

  BMP位图信息头数据用于说明位图的尺寸等信息。

 

  typedef struct tagBITMAPINFOHEADER{

 

  DWORD biSize; // 本结构所占用字节数(14-17字节)

 

  LONG biWidth; // 位图的宽度,以像素为单位(18-21字节)

 

  LONG biHeight; // 位图的高度,以像素为单位(22-25字节)

 

  WORD biPlanes; // 目标设备的级别,必须为1(26-27字节)

 

  WORD biBitCount;// 每个像素所需的位数,必须是1(双色),(28-29字节)

 

  // 4(16色),8(256色)或24(真彩色)之一

 

  DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),(30-33字节)

 

  // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一

 

  DWORD biSizeImage; // 位图的大小,以字节为单位(34-37字节)

 

  LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数(38-41字节)

 

  LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数(42-45字节)

 

  DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数(46-49字节)

 

  DWORD biClrImportant;// 位图显示过程中重要的颜色数(50-53字节)

 

  } BITMAPINFOHEADER;

 

  4:颜色表

 

  颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:

 

  typedef struct tagRGBQUAD {

 

  BYTE rgbBlue;// 蓝色的亮度(值范围为0-255)

 

  BYTE rgbGreen; // 绿色的亮度(值范围为0-255)

 

  BYTE rgbRed; // 红色的亮度(值范围为0-255)

 

  BYTE rgbReserved;// 保留,必须为0

 

  } RGBQUAD;

 

  颜色表中RGBQUAD结构数据的个数有biBitCount来确定:

 

  当biBitCount=1,4,8时,分别有2,16,256个表项;

 

  当biBitCount=24时,没有颜色表项。

 

  位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:

 

  typedef struct tagBITMAPINFO {

 

  BITMAPINFOHEADER bmiHeader; // 位图信息头

 

  RGBQUAD bmiColors[1]; // 颜色表

 

  } BITMAPINFO;

 

  5:位图数据

 

  位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:

 

  当biBitCount=1时,8个像素占1个字节;

 

  当biBitCount=4时,2个像素占1个字节;

 

  当biBitCount=8时,1个像素占1个字节;

 

  当biBitCount=24时,1个像素占3个字节;

 

  Windows规定一个扫描行所占的字节数必须是

 

  4的倍数(即以long为单位),不足的以0填充,

 

  biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;

 

  具体数据举例:

 

  如某BMP文件开头:

 

  424D 4690 0000 0000 0000 4600 0000 2800 0000 8000 0000 9000 0000 0100*1000 0300 0000 0090 0000 A00F 0000 A00F 0000 0000 0000 0000 0000*00F8 E007 1F00 0000*02F1 84F1 04F1 84F1 84F1 06F2 84F1 06F2 04F2 86F2 06F2 86F2 86F2 .... ....

 

  BMP文件可分为四个部分:位图文件头、位图信息头、彩色板、图像数据阵列,在上图中已用*分隔。

图像文件头

  1)1:(这里的数字代表的是"字",即两个字节,下同)图像文件头。0x424D=’BM’,表示是Windows支持的BMP格式。

 

  2)2-3:整个文件大小。4690 0000,为00009046h=36934。

 

  3)4-5:保留,必须设置为0。

 

  4)6-7:从文件开始到位图数据之间的偏移量。4600 0000,为00000046h=70,上面的文件头就是35字=70字节。

位图信息头

  5)8-9:位图图信息头长度。

 

  6)10-11:位图宽度,以像素为单位。8000 0000,为00000080h=128。

 

  7)12-13:位图高度,以像素为单位。9000 0000,为00000090h=144。

 

  8)14:位图的位面数,该值总是1。0100,为0001h=1。

 

  9)15:每个像素的位数。有1(单色),4(16色),8(256色),16(64K色,高彩色),24(16M色,真彩色),32(4096M色,增强型真彩色)。1000为0010h=16。

 

  10)16-17:压缩说明:有0(不压缩),1(RLE 8,8位RLE压缩),2(RLE 4,4位RLE压缩,3(Bitfields,位域存放)。RLE简单地说是采用像素数+像素值的方式进行压缩。T408采用的是位域存放方式,用两个字节表示一个像素,位域分配为r5b6g5。图中0300 0000为00000003h=3。

 

  11)18-19:用字节数表示的位图数据的大小,该数必须是4的倍数,数值上等于(≥位图宽度的最小的4的倍数)×位图高度×每个像素位数。0090 0000为00009000h=80×90×2h=36864。

 

  12)20-21:用象素/米表示的水平分辨率。A00F 0000为0000 0FA0h=4000。

 

  13)22-23:用象素/米表示的垂直分辨率。A00F 0000为0000 0FA0h=4000。

 

  14)24-25:位图使用的颜色索引数。设为0的话,则说明使用所有调色板项。

 

  15)26-27:对图象显示有重要影响的颜色索引的数目。如果是0,表示都重要。

彩色板

  16)28-....(不确定):彩色板规范。对于调色板中的每个表项,用下述方法来描述RGB的值:

 

  1字节用于蓝色分量

 

  1字节用于绿色分量

 

  1字节用于红色分量

 

  1字节用于填充符(设置为0)

 

  对于24-位真彩色图像就不使用彩色板,因为位图中的RGB值就代表了每个象素的颜色。

/**********************************以下是关于结构体对齐的内容【转载来的】*******************************/

(1)什么是字节对齐

  一个变量占用 n 个字节,则该变量的起始地址必须能够被 n 整除,即: 存放起始地址 % n = 0, 对于结构体而言,这个 n 取其成员种的数据类型占空间的值最大的那个。

(2)为什么要字节对齐

  内存空间是按照字节来划分的,从理论上说对内存空间的访问可以从任何地址开始,但是在实际上不同架构的CPU为了提高访问内存的速度,就规定了对于某些类型的数据只能从特定的起始位置开始访问。这样就决定了各种数据类型只能按照相应的规则在内存空间中存放,而不能一个接一个的顺序排列。

  举个例子,比如有些平台访问内存地址都从偶数地址开始,对于一个int型(假设32位系统),如果从偶数地址开始的地方存放,这样一个读周期就可以读出这个int数据,但是如果从奇数地址开始的地址存放,就需要两个读周期,并对两次读出的结果的高低字节进行拼凑才能得到这个int数据,这样明显降低了读取的效率。

(3)如何进行字节对齐

  每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(不指定则取默认值)中较小的一个对齐,并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。

  这个规则有点苦涩,可以把这个规则分解一下,前半句的意思先获得对齐值后与指定对齐值进行比较,其中对齐值获得方式如下:

  1. 数据类型的自身对齐值为:对于char型数据,其自身对齐值为1,对于short型为2,对于int, long, float类型,其自身对齐值为4,对于 double 类型其自身对齐值为8,单位为字节。

  2.结构体自身对齐值:其成员中自身对齐值最大的那个值。

  其中指定对齐值获得方式如下:

  #pragma pack (value)时的指定对齐值value。

  未指定则取默认值。

  后半句的意思是主要是针对于结构体的长度而言,因为针对数据类型的成员,它仅有一个对齐参数,其本身的长度、于这个对齐参数,即1倍。对于结构体而言,它可能使用了多种数据类型,那么这句话翻译成对齐规则: 每个成员的起始地址 % 自身对齐值 = 0,如果不等于 0 则先补空字节直至这个表达式成立。

换句话说,对于结构体而言,结构体在在内存的存放顺序用如下规则即可映射出来:

  (一)每个成员的起始地址 % 每个成员的自身对齐值 = 0,如果不等于 0 则先补空字节直至这个表达式成立;

  (二)结构体的长度必须为结构体的自身对齐值的整数倍,不够就补空字节。

 

  举个例子:

#pragmapack(8)
structA{
  chara;
  longb;
};
structB{
  chara;
  structAb;
  longc;
};

  对于 struct A 来说,对于char型数据,其自身对齐值为1,对于long类型,其自身对齐值为4, 结构体的自身对齐值取其成员最大的对齐值,即大小4。那么struct A 在内存中的顺序步骤为:

  (1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。

  (2) long b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0, 所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x0007.

  (3)此时成员都存放结束,结构体长度为8,为结构体自身对齐值的2倍,符合条件(二).

  此时满足条件(一)和条件(二),struct A 中各成员在内存中的位置为:a*** b ,sizeof(struct A) = 8。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占四位)

  对于struct B,里面有个类型为struct A的成员b自身对齐值为4,对于long类型,其自身对齐值为4. 故struct B的自身对齐值为4。那么structB 在内存中的顺序步骤为:

  (1) char a, 地址范围为0x0000~0x0000,起始地址为0x0000,满足 0x0000 % 1 = 0,这个成员字节对齐了。

  (2) struct A b, 地址起始位置不能从0x00001开始,因为 0x0001 % 4 != 0, 所以先补空字节,直到0x00003结束,即补3个字节的空字节,从0x00004开始存放b,其地址范围为0x00004~0x00011.

  (3) long c,地址起始位置从0x000012开始, 因为 0x0012 % 4 = 0,其地址范围为0x00012~0x0015.

  (4)此时成员都存放结束,结构体长度为16,为结构体自身对齐值的4倍,符合条件(二).

  此时满足条件(一)和条件(二),struct B 中各成员在内存中的位置为:a*** b c ,sizeof(struct C) = 24。(每个星号代表一位,成员各自代表自己所占的位,比如a占一位,b占八位,c占四位)

posted @ 2012-03-24 23:06  maliqian  阅读(2455)  评论(0编辑  收藏  举报