自定义数据包时的字节对齐

struct {}node;

32为的x86,window下VC下sizeof(node)的值为1,而linux的gcc下值为0;


一、WINDOWS下(VC--其实GCC和其原理基本一样,象这种问题,一般要查具体的编译器设置)字节对齐的规则:

1、一般设置的对齐方式为1,2,4字节对齐方式,VC一般默认为4字节(最大为8字节)。结构的首地址必须是结构内最宽类型的整数倍地址;另外,结构体的每一个成员起始地址必须是自身类型大小的整数倍(需要特别注意的是windows下是这样的,但在linux的gcc编译器下最高为4字节对齐),否则在前一类型后补0;这里特别提到的是数组一定要注意,而且在一些编程的技巧中,我们可以使用数组强制字节达到对齐的目的。这在网络编程中是很常见的。

举例:比如CHAR型占用空间为1字节,则其起始位置必须可被1整除。INT为4字节,其起始位置必须被4带队,依次类推。(我们假定类或结构体的起始位置为0位置,其实编译器是在开辟空间时,会寻找起始位置可被结构内最宽类型整除的地址做为开始地址,因此我们可以假定其为0值,因为这0值可以被任意的类型整除。)

2、结构体的整体大小必须可被对齐值整除,默认4(默认,且结构中的类型大小都小于默认的4)。

3、结构体的整体大小必须可被本结构内的最宽类型整除。(其实和上一条是一样的,但这里独立出来,起注意作用。比如结构体里的有DOUBLE,那么结构的大小最后必须可被8整除)

注意:GCC不是这样,就是最高只能被4整除,它是个死的。

否则(2、3条),编译器会在结构的最后添充一定的特定字符来补齐。

structT 

char ch; 
double d ; 
};

在VC中是16个字节,GCC中为12个字节。

4、对于结构体内嵌套结构体的形势,规定是必须按照基本数据类型来定义,而不能以嵌套结构大小来做为上三种使用的基准。

二、举例:

structA 

int a; 
char b; 
short c; 
}; 
struct B 

char b; 
int a; 
short c; 
}; 
struct C 

double t; 
char b; 
int a; 
short c; 
}; 
struct D 

char b; 
double t; 
int a; 
short c; 
};

在VC中,SIZEOF这四个结构体,分别为:8、12、24、24;

我们先谈第一个,(说明一下,在考虑结构体大小时,我们基本可以忽略起始地址的问题,因为这个编译器会自动为我们做好,见上面的说明),结构体内首先是一个INT的4字节,起始地址假定为0,整除4,其小于等于默认的4字节对齐且0为4(INT的占用空间)的整数倍,所以,其占四个字节;其后为起始地址为5,空间为1个字节的CHAR,小于4且5为1(CHAR占用空间)的整数倍,故占用1个字节,然后是一个起始地址为5占2个字节的SHORT,其小于4,但5不为2位数,故补齐一个字节,从第6个字节开始,占2字节空间。所以共占用4+1+1(补)+2=8;8/4=2;整除,故占用8字节空间。

再谈第2个,CHAR不用解释,占有一个字节空间,且可以被0地址整除。而INT则占4字节空间,所以其必须在CHAR后补齐3字节,到第四个字节,才是INT的真正地址。SHORT也不用说,所以共占有:1+3(补)+4+2=10个字节,但10不能整除4,所以要在结构体最后补齐2字节。故实际占有10+2= 12个字节。

谈第三个,C结构体只是在B结构体前加了一个DOUBLE,其它都一样,按说应该是20个字节啊,但注意我们上面规则的第3条。必须是最宽类型的整数倍,一定要分清,所以得补齐到24,D结构体类似,不再讲。

三、结构体的中含有位域

这个东西用得比较少,但还是总结一下:

如果结构体中含有位域(bit-field),那么VC中准则又要有所更改: 
1)如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止; 
2)如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍; 
3)如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式; 
备注:当两字段类型不一样的时候,对于不压缩方式,例如:

struct N 

char c:2; 
int i:4; 
};

依然要满足不含位域结构体内存对齐准则第2条,i成员相对于结构体首地址的偏移应该是4的整数倍,所以c成员后要填充3个字节,然后再开辟4个字节的空间作为int型,其中4位用来存放i,所以上面结构体在VC中所占空间为8个字节;而对于采用压缩方式的编译器来说,遵循不含位域结构体内存对齐准则第2条,不同的是,如果填充的3个字节能容纳后面成员的位,则压缩到填充字节中,不能容纳,则要单独开辟空间,所以上面结构体N在GCC或者Dev-C++中所占空间应该是4个字节。

4)如果位域字段之间穿插着非位域字段,则不进行压缩; 
备注: 
结构

typedefstruct 

char c:2; 
double i; 
int c2:4; 
}N3;

在GCC下占据的空间为16字节,在VC下占据的空间应该是24个字节。

四、字节对齐的控制方法

主要是使用:

#pragma pack (2) 
struct C 

char b; 
int a; 
short c; 
}; 
#pragma pack ()

大家如果有兴趣,可以自己上机调一下各种对齐方式下的占用空间大小,这里就不再举例。

#pragma pack(push)//保存对齐状态 
#pragmapack(4)//设定为4字节对齐 
struct test 

char m1; 
double m4; 
int m3; 
}; 
#pragmapack(pop)//恢复对齐状态

这里需要注意的是,如果对齐的字节非为1、2、4、8等可整除位数,则自动默认回默认的对齐字节数,这个我没有测试,大家可以试一下,应该没什么问题。

五、多编译器的使用:(其下为转载

为了防止不同编译器对齐不一样,建议在代码里面指定对齐参数

可能重要的一点是关于紧缩结构的。紧缩结构的用途其实最常用的结构对齐选项就是:默认对齐和紧缩。在两个程序,或者两个平台之间传递数据时,我们通常会将数据结构设置为紧缩的。这样不仅可以减小通信量,还可以避免对齐带来的麻烦。假设甲乙双方进行跨平台通信,甲方使用了“/Zp2”这么奇怪的对齐选项,而乙方的编译器不支持这种对齐方式,那么乙方就可以理解什么叫欲哭无泪了。当我们需要一个字节一个字节访问结构数据时,我们通常都会希望结构是紧缩的,这样就不必考虑哪个字节是填充字节了。我们把数据保存到非易失设备时,通常也会采用紧缩结构,既减小存储量,也方便其它程序读出。各编译器都支持结构的紧缩,即连续排列结构的各成员变量,各成员变量之间没有任何填充字节。这时,结构的大小等于各成员变量大小的和。紧缩结构的变量可以放在1n边界,即任意地址边界。在GNU gcc:

typedef structSt2Tag

{

St1 st1;

char ch2;

}

__attribute__((packed)) St2;

在ARMCC:

typedef __packed structSt2Tag

{

St1 st1;

char ch2;

} St2;

在VC:

#pragma pack(1)

typedef structSt2Tag

{

St1 st1;

char ch2;

} St2;

#pragma pack()

针对不同的编译器:

#ifdef __GNUC__

#define GNUC_PACKED__attribute__ ((packed))

#else

#define GNUC_PACKED

#endif

#ifdef __arm

#define ARM_PACKED__packed

#else

#define ARM_PACKED

#endif

#ifdef WIN32

#pragma pack(1)

#endif

typedef ARM_PACKEDstruct St2Tag

{

St1 st1;

char ch2;

}

GNUC_PACKED St2;

#ifdef WIN32

#pragma pack()

#endif

最后记录一个小细节。gcc编译器和VC编译器都支持在紧缩结构中包含非紧缩结构,例如前面例子中的St2可以包含非紧缩的St1。但对于ARM编译器而言,紧缩结构包含的其它结构必须是紧缩的。如果紧缩的St2包含了非紧缩的St1,编译时就会报错:

C语言的字节对齐及#pragma pack的使用

2010-04-16 09:44:33|分类: vc/c/c++ |标签: |字号大中小 订阅

C编译器的缺省字节对齐方式(自然对界)

在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。

在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储(成员之间可能有插入的空字节),第一个成员的地址和整个结构的地址相同。

C编译器缺省的结构成员自然对界条件为“N字节对齐”,N即该成员数据类型的长度。如int型成员的自然对界条件为4字节对齐,而double类型的结构成员的自然对界条件为8字节对齐。若该成员的起始偏移不位于该成员的“默认自然对界条件”上,则在前一个节面后面添加适当个数的空字节。

C编译器缺省的结构整体的自然对界条件为:该结构所有成员中要求的最大自然对界条件。若结构体各成员长度之和不为“结构整体自然对界条件的整数倍,则在最后一个成员后填充空字节。

例子1(分析结构各成员的默认字节对界条界条件和结构整体的默认字节对界条件):

struct Test

char x1; //成员x1为char型(其起始地址必须1字节对界),其偏移地址为0 

char x2; //成员x2为char型(其起始地址必须1字节对界,其偏移地址为1 

float x3; //成员x3为float型(其起始地址必须4字节对界),编译器在x2和x3之间填充了两个空字节,其偏移地址为4 

char x4; //成员x4为char型(其起始地址必须1字节对界),其偏移地址为8 
};

因为Test结构体中,最大的成员为flaotx3,因些此结构体的自然对界条件为4字节对齐。则结构体长度就为12字节,内存布局为1100 1111 1000。

例子2:

#include
//#pragma pack(2)
typedef struct
{
int aa1; //4个字节对齐 1111
char bb1;//1个字节对齐 1
short cc1;//2个字节对齐 011
char dd1; //1个字节对齐 1
} testlength1;
int length1 = sizeof(testlength1); //4个字节对齐,占用字节1111 10111000,length = 12

typedef struct
{
char bb2;//1个字节对齐 1
int aa2; //4个字节对齐 01111
short cc2;//2个字节对齐 11
char dd2; //1个字节对齐 1
} testlength2;
int length2 = sizeof(testlength2); //4个字节对齐,占用字节1011 11111000,length = 12


typedef struct
{
char bb3; //1个字节对齐 1
char dd3; //1个字节对齐 1
int aa3; //4个字节对齐 001111
short cc23//2个字节对齐 11

} testlength3;
int length3 = sizeof(testlength3); //4个字节对齐,占用字节1100 11111100,length = 12


typedef struct
{
char bb4; //1个字节对齐 1
char dd4; //1个字节对齐 1
short cc4;//2个字节对齐 11
int aa4; //4个字节对齐 1111
} testlength4;
int length4 = sizeof(testlength4); //4个字节对齐,占用字节1111 1111,length =8


int main(void)
{
printf("length1 = %d.\n",length1);
printf("length2 = %d.\n",length2);
printf("length3 = %d.\n",length3);
printf("length4 = %d.\n",length4);
return 0;
}

改变缺省的对界条件(指定对界) 
· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。 
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。

这时,对齐规则为:

1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragmapack指定的数值和这个数据成员自身长度中,比较小的那个进行。

2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragmapack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

结合1、2推断:当#pragmapack的n值等于或超过所有数据成员长度的时候,这个n值的大小将不产生任何效果。

因此,当使用伪指令#pragma pack(2)时,Test结构体的大小为8,内存布局为11 11 11 10。

需要注意一点,当结构体中包含一个子结构体时,子结构中的成员按照#pragmapack指定的数值和子结构最大数据成员长度中,比较小的那个进行进行对齐。例子如下:

#pragmapack(8) 
struct s1{ 
short a; 
long b; 
};

structs2{ 
char c; 
s1 d; 
long long e; 
}; 
#pragma pack()

sizeof(s2)的结果为24。S1的内存布局为1100 1111,S2的内存布局为1000 1100 1111 0000 11111111。

例子:

#include
#pragma pack(2)
typedef struct
{
int aa1; //2个字节对齐 1111
char bb1;//1个字节对齐 1
short cc1;//2个字节对齐 011
char dd1; //1个字节对齐 1
} testlength1;
int length1 = sizeof(testlength1); //2个字节对齐,占用字节11 11 10 1110,length = 10

typedef struct
{
char bb2;//1个字节对齐 1
int aa2; //2个字节对齐 01111
short cc2;//2个字节对齐 11
char dd2; //1个字节对齐 1
} testlength2;
int length2 = sizeof(testlength2); //2个字节对齐,占用字节10 11 11 1110,length = 10


typedef struct
{
char bb3; //1个字节对齐 1
char dd3; //1个字节对齐 1
int aa3; //2个字节对齐 11 11
short cc23//2个字节对齐 11

} testlength3;
int length3 = sizeof(testlength3); //2个字节对齐,占用字节11 11 11 11,length= 8


typedef struct
{
char bb4; //1个字节对齐 1
char dd4; //1个字节对齐 1
short cc4;//2个字节对齐 11
int aa4; //2个字节对齐 11 11
} testlength4;
int length4 = sizeof(testlength4); //2个字节对齐,占用字节11 11 11 11,length= 8


int main(void)
{
printf("length1 = %d.\n",length1);
printf("length2 = %d.\n",length2);
printf("length3 = %d.\n",length3);
printf("length4 = %d.\n",length4);
return 0;
}

另外,还有如下的一种方式:

· __attribute((aligned(n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。

· __attribute__((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

以上的n = 1, 2, 4, 8,16... 第一种方式较为常见。

posted @ 2013-06-17 16:14 木子你妹 阅读(8)评论(0)  编辑

说明:

结构体的sizeof值,并不是简单的将其中各元素所占字节相加,而是要考虑到存储空间的字节对齐问题。这些问题在平时编程的时候也确实不怎么用到,但在一些笔试面试题目中出是常常出现,对sizeof我们将在另一篇文章中总结,这篇文章我们只总结结构体的sizeof,报着不到黄河心不死的决心,终于完成了总结,也算是小有收获,拿出来于大家分享,如果有什么错误或者没有理解透的地方还望能得到提点,也不至于误导他人。

别忘了这里 http://pppboy.blog.163.com/blog/static/30203796201082494026399/

一、解释

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。

二、准则

其实字节对齐的细节和具体编译器实现相关,但一般而言,满足三个准则:

1.结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

2.结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;

3.结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

三、基本概念


字节对齐:计算机存储系统中以Byte为单位存储数据,不同数据类型所占的空间不同,如:整型(int)数据占4个字节,字符型(char)数据占一个字节,短整型(short)数据占两个字节,等等。计算机为了快速的读写数据,默认情况下将数据存放在某个地址的起始位置,如:整型数据(int)默认存储在地址能被4整除的起始位置,字符型数据(char)可以存放在任何地址位置(被1整除),短整型(short)数据存储在地址能被2整除的起始位置。这就是默认字节对齐方式。

四、结构体长度求法

1.成员都相同时(或含数组且数组数据类型同结构体其他成员数据类型): 
结构体长度=成员数据类型长度×成员个数(各成员长度之和); 
结构体中数组长度=数组数据类型长度×数组元素个数;

2.成员不同且不含其它结构体时; 
(1).分析各个成员长度; 
(2).找出最大长度的成员长度M(结构体的长度一定是该成员的整数倍); 
(3).并按最大成员长度出现的位置将结构体分为若干部分; 
(4).各个部分长度一次相加,求出大于该和的最小M的整数倍即为该部分长度 
(5).将各个部分长度相加之和即为结构体长度

3.含有其他结构体时: 
(1).分析各个成员长度; 
(2).对是结构体的成员,其长度按b来分析,且不会随着位置的变化而变化; 
(3).分析各个成员的长度(成员为结构体的分析其成员长度),求出最大值; 
(4).若长度最大成员在为结构体的成员中,则按结构体成员为分界点分界; 
其他成员中有最大长度的成员,则该成员为分界点; 
求出各段长度,求出大于该和的最小M的整数倍即为该部分长度 
(5).将各个部分长度相加之和即为结构体长度

五、空结构体

struct S5 { };  
sizeof( S5 ); // 结果为1

“空结构体”(不含数据成员)的大小不为0,而是1。试想一个“不占空间”的变量如何被取地址、两个不同的“空结构体”变量又如何得以区分呢于是,“空结构体”变量也得被存储,这样编译器也就只能为其分配一个字节的空间用于占位了。

六、有static的结构体

struct S4{ 
    char a; 
    long b; 
    static long c; //静态 
}; 

静态变量存放在全局数据区内,而sizeof计算栈中分配的空间的大小,故不计算在内,S4的大小为4+4=8。

七、举例说明

1.举例1


很显然默认对齐方式会浪费很多空间,例如如下结构:

struct student 
{ 
    char name[5]; 
    int num; 
    short score; 
}


本来只用了11bytes(5+4+2)的空间,但是由于int型默认4字节对齐,存放在地址能被4整除的起始位置,即:如果name[5]从0开始存放,它占5bytes,而num则从第8(偏移量)个字节开始存放。所以sizeof(student)=16。于是中间空出几个字节闲置着。但这样便于计算机快速读写数据,是一种以空间换取时间的方式。其数据对齐如下图:

|char|char|char|char| 
|char|----|----|----| 
|--------int--------| 
|--short--|----|----| 

如果我们将结构体中变量的顺序改变为:

struct student 
{ 
    int num; 
    char name[5]; 
    short score; 
}


则,num从0开始存放,而name从第4(偏移量)个字节开始存放,连续5个字节,score从第10(偏移量)开始存放,故sizeof(student)=12。其数据对齐如下图:

|--------int--------| 
|char|char|char|char| 
|char|----|--short--| 

如果我们将结构体中变量的顺序再次改为为:

struct student 
{ 
    int num; 
    short score; 
    char name[5]; 
}


则,sizeof(student)=12。其数据对齐如下图:

|--------int--------| 
|--short--|char|char| 
|char|char|char|----| 
2.举例2

(1)

struct test1 
  { int a; 
   int b[4]; 
  };


sizeof(test1)=sizeof(int)+4*sizeof(int)=4+4*4=20;

(2)

 struct test2 
  { char a; 
   int b; 
   double c; 
   bool d; 
  };

分析:该结构体最大长度double型,长度是8,因此结构体长度分两部分: 
第一部分是a、 b、c的长度和,长度分别为1,4,8,则该部分长度和为13,取8的大于13的最小倍数为16; 
第二部分为d,长度为1,取大于1的8的最小倍数为8, 
两部分和为24,故sizeof(test2)=24;

(3)

 struct test3 
{ 
 char a; 
 test2 bb;//见上题 
 int cc; 
}


分析:该结构体有三个成员,其中第二个bb是类型为test2的结构体,长度为24,且该结构体最大长度成员类型为double型,以后成员中没有double型,所以按bb分界为两部分: 
第一部分有a、bb两部分,a长度为1,bb长度为24,取8的大于25的最小倍数32; 
第二部分有cc,长度为4,去8的大于4的最小倍数为8; 
两部分之和为40,故sizeof(test3)=40;


(4)

struct test4 
{ 
 char a; 
 int b; 
}; 
struct test5 
{ char c; 
 test4 d; 
 double e; 
 bool f; 
};


求sizeof(test5) 
分析:test5明显含有结构体test4,按例2容易知道sizeof(test4)=8,且其成员最大长度为4;则结构体test5的最大成员长度为8(double型),考试.大提示e是分界点,分test5为两部分: 
第一部分由c、d、e组成,长度为1、8、8,故和为17,取8的大于17的最小倍数为24; 
第二部分由f组成,长度为1,取8的大于1的最小倍数为8, 
两部分和为32,故sizeof(test5)=24+8=32;

八、union

union的长度取决于其中的长度最大的那个成员变量的长度。即union中成员变量是重叠摆放的,其开始地址相同。

其实union(共用体)的各个成员是以同一个地址开始存放的,每一个时刻只可以存储一个成员,这样就要求它在分配内存单元时候要满足两点:   
 1.一般而言,共用体类型实际占用存储空间为其最长的成员所占的存储空间;   
 2.若是该最长的存储空间对其他成员的元类型(如果是数组,取其类型的数据长度,例int  a[5]为4)不满足整除关系,该最大空间自动延伸;  
 我们来看看这段代码:   

  union   mm{    
  char   a;//元长度1    
  int   b[5];//元长度4    
  double   c;//元长度8    
  int   d[3];    
  };   

本来mm的空间应该是sizeof(int)*5=20;但是如果只是20个单元的话,那可以存几个double型(8位)呢?两个半?当然不可以,所以mm的空间延伸为既要大于20,又要满足其他成员所需空间的整数倍,即24    
所以union的存储空间先看它的成员中哪个占的空间最大,拿他与其他成员的元长度比较,如果可以整除就行

九、指定对界

#pragma pack()命令

如何修改编译器的默认对齐值? 
1.在VC IDE中,可以这样修改:[Project]|[Settings],c/c++选项卡Category的CodeGeneration选项的Struct MemberAlignment中修改,默认是8字节。 
2.在编码时,可以这样动态修改:#pragma pack .注意:是pragma而不是progma.

一般地,可以通过下面的方法来改变缺省的对界条件: 
使用伪指令#pragma pack (n),编译器将按照n个字节对齐; 
使用伪指令#pragma pack (),取消自定义字节对齐方式。

注意:如果#pragmapack(n)中指定的n大于结构体中最大成员size,则其不起作用,结构体仍然按照size最大的成员进行对界。

为了节省空间,我们可以在编码时通过#pragmapack()命令指定程序的对齐方式,括号中是对齐的字节数,若该命令括号中的内容为空,则为默认对齐方式。例如,对于上面第一个结构体,如果通过该命令手动设置对齐字节数如下:

#pragma pack(2) //设置2字节对齐

struct strdent  
{  
    char name[5]; //本身1字节对齐,比2字节对齐小,按1字节对齐  
    int num;          //本身4字节对齐,比2字节对齐大,按2字节对齐  
    short score;    //本身也2字节对齐,仍然按2字节对齐  
} 


#pragma pack() //恢复先前的pack设置,取消设置的字节对齐方式

则,num从第6(偏移量)个字节开始存放,score从第10(偏移量)个字节开始存放,故sizeof(student)=12,其数据对齐如下图:

|char|char|  
|char|char|  
|char|----| 
|----int--| 
|----int--| 
|--short--| 

这样改变默认的字节对齐方式可以更充分地利用存储空间,但是这会降低计算机读写数据的速度,是一种以时间换取空间的方式。

十、代码验证

  • 代码
//------------------------------------ 
// 环境:VS2005 
// 时间:2010.9.24 
// 用途:结构体大小测试 
// 作者:pppboy.blog.163.com
//----------------------------------- 
#include "stdafx.h" 
#include  
using namespace std; 
//空 
struct S0{  }; 
struct S1{ 
    char a; 
    long b; 
}; 
struct S2{ 
    long b; 
    char a; 
}; 
struct S3 { 
    char c; 
    struct S1 d;//结构体 
    long e; 
}; 
struct S4{ 
    char a; 
    long b; 
    static long c; //静态 
}; 
struct S5{ 
    char a; 
    long b; 
    char name[5]; //数组 
}; 
//含有一个数组 
struct S6{ 
    char a; 
    long b; 
    int name[5]; //数组 
}; 
struct student0 
{ 
    char name[5]; 
    int num; 
    short score; 
}; 
struct student1 
{ 
    int num; 
    char name[5]; 
    short score; 
}; 
struct student2 
{ 
    int num; 
    short score; 
    char name[5]; 
}; 
union union1 
{ 
    long a; 
    double b; 
    char name[9]; 
}; 
union   union2{    
    char a; 
    int b[5];  
    double  c; 
    int d[3];  
};    
int main(int argc, char* argv[]) 
{ 
    cout << "char: " << sizeof(char) << endl; //1 
    cout << "long: " << sizeof(long) << endl; //4 
    cout << "int:  " << sizeof(int) << endl; //4 
    cout << "S0: " << sizeof(S0) << endl; //1 
    cout << "S1: " << sizeof(S1) << endl; //8 
    cout << "S2: " << sizeof(S2) << endl; //8 
    cout << "S3: " << sizeof(S3) << endl; //24 
    cout << "S4: " << sizeof(S4) << endl; //8 
    cout << "S5: " << sizeof(S5) << endl; //16 
    cout << "S6: " << sizeof(S6) << endl; //28 
    cout << "union1 :" << sizeof(union1) << endl; 
    cout << "union2 :" << sizeof(union2) << endl; 
    cout << "student0: " << sizeof(student0) << endl; 
    cout << "student1: " << sizeof(student1) << endl; 
    cout << "student2: " << sizeof(student2) << endl; 
    system("pause"); 
    return 0; 
} 
  • 输出

//这是默认的结果(8字节对齐)

char: 1 
long: 4 
int:  4 
S0: 1 
S1: 8 
S2: 8 
S3: 16 
S4: 8 
S5: 16 
S6: 28 
union1 :16 
union2 :24 
student0: 16 
student1: 12 
student2: 12 
请按任意键继续. . .

//这是16字节对齐的结果,可以看到当设置16字节对齐时,确实没什么效果,里面最大的是double,也就是8字节,#pragmapack (n)中指定的n大于结构体中最大成员size,则其不起作用。

char: 1 
long: 4 
int:  4 
double:8 
S0: 1 
S1: 8 
S2: 8 
S3: 16 
S4: 8 
S5: 16 
S6: 28 
union1 :16 
union2 :24 
student0: 16 
student1: 12 
student2: 12 
请按任意键继续. . .

//这是2字节对齐的结果,可以慢慢参考研究

char: 1 
long: 4 
int:  4 
double:8 
S0: 1 
S1: 6 
S2: 6 
S3: 12 
S4: 6 
S5: 12 
S6: 26 
union1 :10 
union2 :20 
student0: 12 
student1: 12 
student2: 12 
请按任意键继续. . .
  • 说明:

(1)默认8字节对齐

(2)分析

S0:空

S1:

|char|----|----|----| 
|-------long--------|

S2:

|-------long--------| 
|char|----|----|----|

S3:

其中包含的S1中最长的为long,S3中也为long,以最长的为分界,那么为:1+8+4 =13,那么这个结构体的长度就是8的倍数16。

内存是怎么样的现在还没有弄清楚。。。

S4:

静态变量存放在全局数据区内,而sizeof计算栈中分配的空间的大小,故不计算在内,S4的大小为4+4=8。

S5,S6,Student见上面例子。

union1:

最长double=8,但charc[9]用9个不够,再加一倍到16.

union2:

类型最长的是long=8,变量最长的是intb[5] = 4*5=20,20以上8的倍数为24。

十一、还没有解决的问题

虽然知道结构体中含有结构体的长度怎么计算,但不知道它的内存是什么样子的,在VS中用

cout << "&objS3.a: "<< hex  << &objS3.a << endl; 

为什么显示出来是乱码??

十二、字节对齐可能带来的隐患

(说明:从一个pdf复制,参考一下)

代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如:

unsigned int i = 0x12345678; 
unsigned char *p=NULL; 
unsigned short *p1=NULL; 
p=&i; 
*p=0x00; 
p1=(unsigned short *)(p+1); 
*p1=0x0000;


最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。 
在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐。

十三、参考引用

在上述内容中,引用参考了不少文章,现将链接给出,同时感谢Scorpions带来的音乐快感。这里仅供本人学习,谢谢作者。

http://blog.csdn.net/houghstc/archive/2009/06/30/4307523.aspx

http://blog.csdn.net/vincent_1011/archive/2009/08/25/4479965.aspx

http://www.baidu.com/index.php

http://apps.hi.baidu.com/share/detail/6503863

http://hmmanhui.blog.sohu.com/108007380.html

http://www.cppreference.com/wiki/keywords/sizeof

http://blog.csdn.net/goodluckyxl/archive/2005/10/17/506827.aspx

本文章出处:
http://pppboy.blog.163.com/blog/static/30203796201082494026399/

猜你喜欢

转载自blog.csdn.net/sinat_27261621/article/details/52798361