王者荣耀李白模型重做重做李白 一技能还有吗

c/c++(61)
复习总结(19)
面试(21)
结构体的sizeof值,并不是简单的将其中各元素所占字节相加,而是要考虑到存储空间的字节对齐问题。这些问题在平时编程的时候也确实不怎么用到,但在一些笔试面试题目中出是常常出现,对sizeof我们将在另一篇文章中总结,这篇文章我们只总结结构体的sizeof,报着不到黄河心不死的决心,终于完成了总结,也算是小有收获,拿出来于大家分享,如果有什么错误或者没有理解透的地方还望能得到提点,也不至于误导他人。
别忘了这里&
现代计算机中内存空间都是按照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{
static long //静态 };
静态变量存放在全局数据区内,而sizeof计算栈中分配的空间的大小,故不计算在内,S4的大小为4+4=8。
七、举例说明
很显然默认对齐方式会浪费很多空间,例如如下结构:&
struct student {
char name[5];
本来只用了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 {
char name[5];
则,num从0开始存放,而name从第4(偏移量)个字节开始存放,连续5个字节,score从第10(偏移量)开始存放,故sizeof(student)=12。其数据对齐如下图:
|--------int--------| |char|char|char|char| |char|----|--short--|
如果我们将结构体中变量的顺序再次改为为:&
struct student {
char name[5]; }
则,sizeof(student)=12。其数据对齐如下图:
|--------int--------| |--short--|char|char| |char|char|char|----|
struct test1   { int    int b[4];   };
sizeof(test1)=sizeof(int)+4*sizeof(int)=4+4*4=20;
struct test2   { char    int    double    bool   };
分析:该结构体最大长度double型,长度是8,因此结构体长度分两部分:&
第一部分是a、 b、 c的长度和,长度分别为1,4,8,则该部分长度和为13,取8的大于13的最小倍数为16;&
第二部分为d,长度为1,取大于1的8的最小倍数为8,&
两部分和为24,故sizeof(test2)=24;
struct test3 {
test2//见上题
分析:该结构体有三个成员,其中第二个bb是类型为test2的结构体,长度为24,且该结构体最大长度成员类型为double型,以后成员中没有double型,所以按bb分界为两部分:
第一部分有a 、bb两部分,a长度为1,bb长度为24,取8的大于25的最小倍数32;&
第二部分有cc,长度为4,去8的大于4的最小倍数为8;&
两部分之和为40,故sizeof(test3)=40;
struct test4 {
int }; struct test5 { char
求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(共用体)的各个成员是以同一个地址开始存放的,每一个时刻只可以存储一个成员,这样就要求它在分配内存单元时候要满足两点:&&&
& 1.一般而言,共用体类型实际占用存储空间为其最长的成员所占的存储空间;&&&
& 2.若是该最长的存储空间对其他成员的元类型(如果是数组,取其类型的数据长度,例int&& a[5]为4)不满足整除关系,该最大空间自动延伸;&&&
& 我们来看看这段代码:&&&&
char//元长度1
b[5];//元长度4
double//元长度8
本来mm的空间应该是sizeof(int)*5=20;但是如果只是20个单元的话,那可以存几个double型(8位)呢?两个半?当然不可以,所以mm的空间延伸为既要大于20,又要满足其他成员所需空间的整数倍,即24&&&
所以union的存储空间先看它的成员中哪个占的空间最大,拿他与其他成员的元长度比较,如果可以整除就行。
九、指定对界
#pragma pack()命令
如何修改编译器的默认对齐值?&
1.在VC IDE中,可以这样修改:[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改,默认是8字节。
2.在编码时,可以这样动态修改:#pragma pack .注意:是pragma而不是progma.
一般地,可以通过下面的方法来改变缺省的对界条件:&
使用伪指令#pragma pack (n),编译器将按照n个字节对齐;&
使用伪指令#pragma pack (),取消自定义字节对齐方式。
注意:如果#pragma pack (n)中指定的n大于结构体中最大成员size,则其不起作用,结构体仍然按照size最大的成员进行对界。
为了节省空间,我们可以在编码时通过#pragma pack()命令指定程序的对齐方式,括号中是对齐的字节数,若该命令括号中的内容为空,则为默认对齐方式。例如,对于上面第一个结构体,如果通过该命令手动设置对齐字节数如下:
#pragma pack(2) //设置2字节对齐&
struct strdent
char name[5]; //本身1字节对齐,比2字节对齐小,按1字节对齐
//本身4字节对齐,比2字节对齐大,按2字节对齐
//本身也2字节对齐,仍然按2字节对齐
#pragma pack() // 恢复先前的pack设置,取消设置的字节对齐方式
则,num从第6(偏移量)个字节开始存放,score从第10(偏移量)个字节开始存放,故sizeof(student)=12,其数据对齐如下图:&
|char|char|
|char|char|
|char|----| |----int--| |----int--| |--short--|
这样改变默认的字节对齐方式可以更充分地利用存储空间,但是这会降低计算机读写数据的速度,是一种以时间换取空间的方式。
十、代码验证
//------------------------------------ // 环境:VS2005 // 时间: // 用途:结构体大小测试 // 作者://----------------------------------- #include &stdafx.h& #include &iostream& using namespace //空 struct S0{
}; struct S1{
long }; struct S2{
char }; struct S3 {
struct S1//结构体
long }; struct S4{
static long //静态 }; struct S5{
char name[5]; //数组 }; //含有一个数组 struct S6{
int name[5]; //数组 }; struct student0 {
char name[5];
short }; struct student1 {
char name[5];
short }; struct student2 {
char name[5]; }; union union1 {
char name[9]; }; union
int main(int argc, char* argv[]) {
cout && &char: & && sizeof(char) && //1
cout && &long: & && sizeof(long) && //4
cout && &int:
& && sizeof(int) && //4
cout && &S0: & && sizeof(S0) && //1
cout && &S1: & && sizeof(S1) && //8
cout && &S2: & && sizeof(S2) && //8
cout && &S3: & && sizeof(S3) && //24
cout && &S4: & && sizeof(S4) && //8
cout && &S5: & && sizeof(S5) && //16
cout && &S6: & && sizeof(S6) && //28
cout && &union1 :& && sizeof(union1) &&
cout && &union2 :& && sizeof(union2) &&
cout && &student0: & && sizeof(student0) &&
cout && &student1: & && sizeof(student1) &&
cout && &student2: & && sizeof(student2) &&
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字节,#pragma pack (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字节对齐
|char|----|----|----| |-------long--------|
|-------long--------| |char|----|----|----|
其中包含的S1中最长的为long,S3中也为long,以最长的为分界,那么为:1+8+4 = 13,那么这个结构体的长度就是8的倍数16。
内存是怎么样的现在还没有弄清楚。。。
静态变量存放在全局数据区内,而sizeof计算栈中分配的空间的大小,故不计算在内,S4的大小为4+4=8。
S5,S6,Student见上面例子。
最长double=8,但char c[9]用9个不够,再加一倍到16.
类型最长的是long=8,变量最长的是int b[5] = 4*5=20,20以上8的倍数为24。
十一、还没有解决的问题
虽然知道结构体中含有结构体的长度怎么计算,但不知道它的内存是什么样子的,在VS中用
cout && &&objS3.a: &&& hex
&& &objS3.a &&
为什么显示出来是乱码??
十二、字节对齐可能带来的隐患
(说明:从一个pdf复制,参考一下)
代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如:&
unsigned int i = 0x; 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带来的音乐快感。这里仅供本人学习,谢谢作者。
本文章出处:
&&相关文章推荐
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:138669次
积分:1531
积分:1531
排名:千里之外
原创:12篇
转载:151篇
(1)(15)(35)(6)(1)(1)(2)(2)(5)(5)(10)(11)(6)(15)(1)(5)(5)(5)(10)(8)(2)(3)(12)阅读(...) 评论()& & & & & & & & & & & & & & & & & & & & & & & & &结构体字节对齐
&&&&& 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排列, 而不是简单地顺序排列,这就是内存对齐。
&&&&& 内存对齐的原因:
&&&&& 1)某些平台只能在特定的地址处访问特定类型的数据;
&&&&& 2)提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量;但是若从奇地址单元处存放,则需要2个读取周期读取该变量。
&&&&&& win32平台下的微软C编译器对齐策略:
&&&&& 1)结构体变量的首地址能够被其最宽数据类型成员的大小整除。编译器在为结构体变量开辟空间时,首先找到结构体中最宽的数据类型,然后寻找内存地址能被该数据类型大小整除的位置,这个位置作为结构体变量的首地址。而将最宽数据类型的大小作为对齐标准。
&&&&& 2)结构体每个成员相对结构体首地址的偏移量(offset)都是每个成员本身大小的整数倍,如有需要会在成员之间填充字节。编译器在为结构体成员开辟空 间时,首先检查预开辟空间的地址相对于结构体首地址的偏移量是否为该成员大小的整数倍,若是,则存放该成员;若不是,则填充若干字节,以达到整数倍的要 求。
&&&&& 3)结构体变量所占空间的大小必定是最宽数据类型大小的整数倍。如有需要会在最后一个成员末尾填充若干字节使得所占空间大小是最宽数据类型大小的整数倍。
下面看一下sizeof在计算结构体大小的时候具体是怎样计算的
1.test1&& 空结构体
typedef struct&node
则sizeof(S)=1;或sizeof(S)=0;
在C++中占1字节,而在C中占0字节。
typedef struct&node1
&&&&int&a;
&&&&char&b;
&&&&short&c;
则sizeof(S1)=8。这是因为结构体node1中最长的数据类型是int,占4个字节,因此以4字节对齐,则该结构体在内存中存放方式为
|--------int--------|&& 4字节
|char|----|--short-|&& 4字节
总共占8字节
typedef struct&node2
&&&&char&a;
&&&&int&b;
&&&&short&c;
&则siezof(S3)=12.最长数据类型为int,占4个字节。因此以4字节对齐,其在内存空间存放方式如下:
|char|----|----|----|& 4字节
|--------int--------|& 4字节
|--short--|----|----|& 4字节
总共占12个字节
4.test4& 含有静态数据成员&
typedef struct&node3
&&&&int&a;
&&&&short&b;
&&&&static&int&c;
则sizeof(S3)=8.这里结构体中包含静态数据成员,而静态数据成员的存放位置与结构体实例的存储地址无关(注意只有在C++中结构体中才能含有静态数据成员,而C中结构体中是不允许含有静态数据成员的)。其在内存中存储方式如下:
|--------int--------|&& 4字节
|--short-|----|----|&&& 4字节
而变量c是单独存放在静态数据区的,因此用siezof计算其大小时没有将c所占的空间计算进来。
5.test5& 结构体中含有结构体
typedef struct&node4
&&&&bool&a;
&&&&S1 s1;
&&&&short&b;
则sizeof(S4)=16。是因为s1占8字节,而s1中最长数据类型为int,占4个字节,bool类型1个字节,short占2字节,因此以4字节对齐,则存储方式为
|-------bool--------|& 4字节
|-------s1----------|& 8字节
|-------short-------|& 4字节
typedef struct&node5
&&&&bool&a;
&&&&S1 s1;
&&&&double&b;
&&&&int&c;
则sizeof(S5)=32。是因为s1占8字节,而s1中最长数据类型为int,占4字节,而double占8字节,因此以8字节对齐,则存放方式为:
|--------bool--------|&&& 8字节
|---------s1---------|&&& 8字节
|--------double------|&&& 8字节
|----int----|---------|&&&& 8字节&
若在程序中使用了#pragma pack(n)命令强制以n字节对齐时,默认情况下n为8.
则比较n和结构体中最长数据类型所占的字节大小,取两者中小的一个作为对齐标准。
若需取消强制对齐方式,则可用命令#pragma pack()
如果在程序开头使用命令#pragma pack(4),对于下面的结构体
typedef struct&node5
&&&&bool&a;
&&&&S1 s1;
&&&&double&b;
&&&&int&c;
则sizeof(S5)=24.因为强制以4字节对齐,而S5中最长数据类型为double,占8字节,因此以4字节对齐。在内存中存放方式为:
& |-----------a--------|&& 4字节
& |--------s1----------|&& 4字节
& |--------s1----------|&& 4字节
& |--------b-----------|&& 4字节
& |--------b-----------|&& 4字节
&&|---------c----------|&&& 4字节
总结一下,在计算sizeof时主要注意一下几点:
1)若为空结构体,则只占1个字节的单元
2)若结构体中所有数据类型都相同,则其所占空间为 成员数据类型长度&成员个数
若结构体中数据类型不同,则取最长数据类型成员所占的空间为对齐标准,数据成员包含另一个结构体变量t的话,则取t中最 长数据类型与其他数据成员比较,取最长的作为对齐标准,但是t存放时看做一个单位存放,只需看其他成员即可。
3)若使用了#pragma pack(n)命令强制对齐标准,则取n和结构体中最长数据类型占的字节数两者之中的小者作为对齐标准。
另外除了结构体中存在对齐之外,普通的变量存储也存在字节对齐的情况,即自身对齐。编译器规定:普通变量的存储首地址必须能被该变量的数据类型宽度整除。
测试程序:
#include &iostream&
using&namespace&
typedef&struct&node
typedef&struct&node1
&&&&int&a;
&&&&char&b;
&&&&short&c;
typedef&struct&node2
&&&&char&a;
&&&&int&b;
&&&&short&c;
typedef&struct&node3
&&&&int&a;
&&&&short&b;
&&&&static&int&c;
typedef&struct&node4
&&&&bool&a;
&&&&S1 s1;
&&&&short&b;
typedef&struct&node5
&&&&bool&a;
&&&&S1 s1;
&&&&double&b;
&&&&int&c;
int&main(int&argc,&char&*argv[])
&&&&cout&&sizeof(char)&&" "&&sizeof(short)&&" "&&sizeof(int)&&" "&&sizeof(float)&&" "&&sizeof(double)&&
&&&&S1 s1;
&&&&S2 s2;
&&&&S3 s3;
&&&&S4 s4;
&&&&S5 s5;
&&&&cout&&sizeof(S3)&&
&&&&cout&&sizeof(s)&&" "&&sizeof(s1)&&" "&&sizeof(s2)&&" "&&sizeof(s3)&&" "&&sizeof(s4)&&" "&&sizeof(s5)&&
&&&&return&0;
阅读(...) 评论()1293人阅读
结构体(struct)的sizeof值,并不是简单的将其中各元素所占字节相加,而是要考虑到存储空间的字节对齐问题。先看下面定义的两个结构体.struct{&&&}S1;struct{&char&&char&&}S2;分别用程序测试得出sizeof(S1)=6 , sizeof(S2)=4 可见,虽然两个结构体所含的元素相同,但因为其中存放的元素类型顺序不一样,所占字节也出现差异。这就是字节对齐原因。通过字节对齐,有助于加快计算机的取数速度,否则就得多花指令周期。
字节对齐原则
结构体默认的字节对齐一般满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员自身大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
通过这三个原则,就不难理解上面两个struct的差异了.对于struct S1, 为了使short变量满足字节对其准则(2), 即其存储位置相对于结构体首地址的offset是自身大小(short占2个字节)的整数倍,必须在字节a后面填充一个字节以对齐;再由准则(3),为了 满足结构体总大小为short大小的整数倍,必须再在c后面填充一个字节。对于struct S2, 却不必如上所述的填充字节,因为其直接顺序存储已经满足了对齐准则。
如果将上面两个结构体中的short都改为int(占4个字节), 那么会怎么样呢? 程序得出sizeof(S1)=12, sizeof(S2)=8 利用上面的准则,也不难计算得出这样的结果。S1中在a后面填充3个字节、在c后面填充3个字节,这样一共12个字节;S2中在a、b顺序存储之后填充两个字节用以对其,这样一共就8个字节。当然,在某些时候也可以设置字节对齐方式。这就需要使用 #pragma pack 。#pragma pack(push) //压栈保存#pragma pack(1)// 设置1字节对齐struct{&&&}S1;#pragma pack(pop) // 恢复先前设置
如上所示,将对其方式设为1字节对齐,那么S1就不填充字节,sizeof为各元素所占字节之和即4。这一点在从外部2进制文件中读入struct大小的数据到struct中,是很有用的.
此外,对于空结构体(即内部没有任何元素),在UNIX平台gcc4.1下的sizeof 为0,而在Windows平台VC6下得出的sizeof却为1!我也不知道怎么回事,真是个有趣的现象!
&&相关文章推荐
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:4897次
排名:千里之外

我要回帖

更多关于 王者荣耀李白要重做吗 的文章

 

随机推荐