您的位置:首页 > 其它

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

2013-05-29 11:30 471 查看
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充

最近用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占四位)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: