字节对齐嘛 下面是我复制的 你有了一个double型8个字节,在不用#pragma pack时,大小必是8的倍数
c++内存中字节对齐问题详解
一、什么是字节对齐,为什么要对齐
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问 一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对 数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那 么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。
二、请看下面的结构:
struct MyStruct
{
double dda1;
char dda;
int type
};
对结构MyStruct采用sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求:
sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。你知道为什么在VC中会得出这样一个结果吗?
其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc60,32位系统)。
类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char
偏移量必须为sizeof(char)即1的倍数
int
偏移量必须为sizeof(int)即4的倍数
float
偏移量必须为sizeof(float)即4的倍数
double
偏移量必须为sizeof(double)即8的倍数
Short
偏移量必须为sizeof(short)即2的倍数
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
下面用前面的例子来说明VC到底怎么样来存放结构的。
struct MyStruct
{
double dda1;
char dda;
int type
};
为上面的结构分配空间的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;接下来为第二个成员dda分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地方满足对齐方式,该成员变量占用 sizeof(char)=1个字节;接下来为第三个成员type分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof (int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+ 3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。
下面再举个例子,交换一下上面的MyStruct的成员变量的位置,使它变成下面的情况:
struct MyStruct
{
char dda;
double dda1;
int type
};
这个结构占用的空间为多大呢?在VC60环境下,可以得到sizeof(MyStruc)为24。结合上面提到的分配空间的一些原则,分析下VC怎么样为上面的结构分配空间的。(简单说明)
struct MyStruct
{
char dda;//偏移量为0,满足对齐方式,dda占用1个字节;
double dda1;//下一个可用的地址的偏移量为1,不是sizeof(double)=8
//的倍数,需要补足7个字节才能使偏移量变为8(满足对齐
//方式),因此VC自动填充7个字节,dda1存放在偏移量为8
//的地址上,它占用8个字节。
int type;//下一个可用的地址的偏移量为16,是sizeof(int)=4的倍
//数,满足int的对齐方式,所以不需要VC自动填充,type存
//放在偏移量为16的地址上,它占用4个字节。
};//所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构
//的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof
//(double)=8)的倍数,所以需要填充4个字节,以满足结构的大小为
//sizeof(double)=8的倍数。
所以该结构总的大小为:sizeof(MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有放任何有意义的东西。
VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。
VC 中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;
否则必须为n的倍数。下面举例说明其用法。
#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢复对齐状态
以上结构的大小为16,下面分析其存储情况,首先为m1分配空间,其偏移量为0,满足我们自己设定的对齐方式(4字节对齐),m1占用1个字节。接着开始为 m4分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。如果把上面的#pragma pack(4)改为#pragma pack(16),那么我们可以得到结构的大小为24。(请读者自己分析)
三、再看下面这个例子
#pragma pack(8)
struct S1{
char a;
long b;
};
struct S2 {
char c;
struct S1 d;
long long e;
};
#pragma pack()
sizeof(S2)结果为24
成员对齐有一个重要的条件,即每个成员分别对齐即每个成员按自己的方式对齐
也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节
S1中,成员a是1字节默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;
S2 中,c和S1中的a一样,按1字节对齐,而d 是个结构,它是8个字节,它按什么对齐呢对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4所以,成员d就是按4字节对齐成员e是8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节的空,从第16个字节开始放置成员e这时,长度为24,已经可以被8(成员e按8字节对齐)整除这样,一共使用了24个字节
a b
S1的内存布局:11,1111,
c S1a S1b d
S2的内存布局:1,11,1111,11111111
这里有三点很重要:
1每个成员分别按自己的方式对齐,并能最小化长度。
2复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
3对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。
function DDA(x1,y1,x2,y2,color)
length =abs(x2-x1);
if abs(y2-y1)>length
length=abs(y2-y1);
end
dx=(x2-x1)/length;
dy=(y2-y1)/length;
x=x1+05sign(dx);
y=y1+05sign(dy);
hold on
for i=1:length
plot(round(x),round(y),'Color',color)
x=x+dx;
y=y+dy;
end
hold off
end
DDA自问世以来已经获得极为广泛的应用。裴觉民等[23]将DDA方法应用在裂隙岩体边坡工程中,对原始边坡和开挖后的边坡进行了计算,并考虑了爆破作用对边坡稳定性的影响。TCKe[24]应用DDA方法和极限平衡法研究了某边坡的稳定性,指出了DDA方法的优越性;Xuecheng Dong[25]将 DDA 方法应用在三峡船闸边坡的稳定性研究中;SLZhao等[26]、GQChen 等[27]将 DDA 方法应用在岩石边坡的稳定性分析中;YHHat zor[28]应用关键块体理论和DDA方法研究了Masada山脉某边坡的稳定性及破坏模式;周少怀等[29]基于DDA算法,补充和发展了DDA方法计算机程序,分析了边坡大位移问题和地下开挖引起地面变形的工程实例,并与离散元计算结果进行了比较研究。Kim YongⅡ等(1996)[19]将DDA方法应用在地下工程的开挖及岩体支护设计中,认为所建立的算法可以模拟地下工程的开挖过程。首先要计算出开挖前岩体内的应力状态,其次根据第一次开挖步来确定新产生的应力分布,新产生的应力作为下一次开挖的初始应力,开挖过程结束以前一直进行这样的迭代计算。经过算例研究,认为岩体开挖的最终稳定性与开挖次序及相应的应力历史有关。另外,他们还进行了喷射混凝土和混凝土衬砌方面的研究,将喷射混凝土和混凝土衬砌处理成具有一定厚度和材料性质的单元进行分析研究。邬爱清等[30]根据已初步开发出的DDA模型计算程序,分别就某工程试验洞开挖和边坡明挖问题进行了计算,并与有限元结果进行了比较,结果表明,DDA模型计算结果在岩体开挖位移形态及位移量级上与有限元及实际位移监测结果都具有较好的可比性。
Kuokai Shyu 等[13]应用DDA方法研究了在地震作用下Bartlett 坝肩的稳定性;Shilong Zhao等[31]应用DDA方法研究了岩体的倾倒破坏问题;Lanbo Liu[32]应用DDA方法研究了大地构造学中的板块运动;Takeshi Sasaki等[33]应用有限元方法和DDA方法研究了裂隙岩体地基的稳定性,结果与解析分析极为吻合;CJPearce等(1998)[34]将DDA方法应用在混凝土破裂行为模拟中;YIKim等[35]将DDA方法应用在混凝土坝基的稳定性分析中;SMHsiung[36]等将DDA方法应用在地震荷载对地下工程的稳定性影响中;AMortazavi等[37]将DDA方法应用在矿山岩暴分析中;戴华阳等[38]提出了急倾斜煤层开采地表非连续变形的度量方法。
DDA方法主要是针对岩石介质的,TCKe 等[39]、PAThomas 等[40]、Yuzo Ohnishi等[41]、Kuokai Shyu 等[42]将其应用在土力学中,给出了颗粒体介质DDA的算法;YNOh等[43]将DDA方法应用在海堤基础及地基土的相互作用中;LKChien[44]等将DDA方法应用在海床的冲刷与回填稳定性分析中;张国新等[45]采用正多边形体代替圆形颗粒体来模拟土的应力-应变关系。
国内外学者对DDA的大量研究,使得该方法更加成熟、更加适合于岩体系统的变形分析。由于DDA方法具有完备的块体运动学理论,且将静力分析与动力分析统一起来,因此其具有处理结构工程、岩体力学以及材料分析等方面的能力。但也应该清晰地看到,DDA方法问世仅10余年,在具体应用上仍然存在一些不足。我们知道,DDA方法最初是用于解决岩体不连续变形问题的,其用实际结构面所切割的岩块作为分析单元,但目前国内外在研究与应用DDA方法中均忽略了对结构面的调查与研究,往往用假想的规则的块体单元或者考虑了规模比较大的实际结构面所形成的块体单元进行研究[46],忽略了随机分布的较小规模的结构面,这样的单元仅能用于验证 DDA的有效性,而不能用于实际工程中;另外目前的研究忽略了结构面的充填厚度,实际上结构面的充填物对岩体的稳定性具有重要的影响。从工程实践来看,结构面不论规模大小几乎都有一定程度的充填,结构面的充填物不能像文献[47]等那样简单地理解为软弱夹层,充填的是泥土、碎石土等,事实上,在新鲜岩体内结构面的充填物具有相当高的d性模量,且充填厚度与结构面的规模成一定比例关系[48]。岩体变形包括结构体(块体)变形和结构面变形,因此块体系统的变形不仅发生在块体本身,也包括结构面的变形,而且块体本身的变形往往小于结构面的变形,因此要合理地描述整个块体系统的变形,应该研究结构面的变形对整个块体系统变形的贡献。
以上就是关于C语言seziof求空间问题全部的内容,包括:C语言seziof求空间问题、计算机图形学:Matlab编程画直线(DDA算法)、DDA方法的应用等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)