="tags" href="/tags/C.html" title=c>clearfix">
class="tags" href="/tags/C.html" title=c>content_views"
class="tags" href="/tags/C.html" title=c>class="htmledit_views">
http://www.360doclass="tags" href="/tags/C.html" title=c>c.class="tags" href="/tags/C.html" title=c>com/class="tags" href="/tags/C.html" title=c>content/10/0124/00/722458_14261259.shtml
为了能使CPU对变量进行高效快速的访问class="tags" href="/tags/C.html" title=c>c;变量的起始地址应该具有某些特性class="tags" href="/tags/C.html" title=c>c;即所谓的“对齐”。例如对于4字节的int类型变量class="tags" href="/tags/C.html" title=c>c;其起始地址应位于4字节边界上class="tags" href="/tags/C.html" title=c>c;即起始地址能够被4整除。变量的对齐规则如下(32位系统):
Type
Alignment
class="tags" href="/tags/C.html" title=c>char
在字节边界上对齐
short (16-bit)
在双字节边界上对齐
int and long (32-bit)
在4字节边界上对齐
float
在4字节边界上对齐
double
在8字节边界上对齐
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ctures
单独考虑结构体的个成员class="tags" href="/tags/C.html" title=c>c;它们在不同的字节边界上对齐。
其中最大的字节边界数就是该结构的字节边界数。
如果结构体中有结构体成员class="tags" href="/tags/C.html" title=c>c;那么这是一个递归的过程。
class="tags" href="/tags/C.html" title=c>color: #0000ff;">设class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器设定的最大对齐字节边界数为nclass="tags" href="/tags/C.html" title=c>c;对于结构体中的某一成员itemclass="tags" href="/tags/C.html" title=c>c;它相对于结构首地址的实际字节对齐数
class="tags" href="/tags/C.html" title=c>color: #0000ff;">目X应该满足以下规则:
X = min(n, sizeof(item))
例如class="tags" href="/tags/C.html" title=c>c;对于结构体
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct {
class="tags" href="/tags/C.html" title=c>char a;
long b;
} T;
当位于32位系统class="tags" href="/tags/C.html" title=c>c;n=8时:
a的偏移为0class="tags" href="/tags/C.html" title=c>c;
b的偏移为4class="tags" href="/tags/C.html" title=c>c;中间填充了3个字节, b的X为4;
当位于32位系统class="tags" href="/tags/C.html" title=c>c;n=2时:
a的偏移为0class="tags" href="/tags/C.html" title=c>c;
b的偏移为2class="tags" href="/tags/C.html" title=c>c;中间填充了1个字节class="tags" href="/tags/C.html" title=c>c;b的X为2;
结构体的sizeof:
设结构体的最后一个成员为LastItemclass="tags" href="/tags/C.html" title=c>c;其相对于结构体首地址的偏移为offset(LastItem)class="tags" href="/tags/C.html" title=c>c;其大小为 sizeof(LastItem)class="tags" href="/tags/C.html" title=c>c;结构体的字节对齐数为Nclass="tags" href="/tags/C.html" title=c>c;则:结构体的sizeof 为: 若offset(LastItem)+ sizeof(LastItem)能够被N整除class="tags" href="/tags/C.html" title=c>c;那么就是offset(LastItem)+ sizeof(LastItem)class="tags" href="/tags/C.html" title=c>c;否则class="tags" href="/tags/C.html" title=c>c;在后面填充class="tags" href="/tags/C.html" title=c>c;直到能够被N整除。
另外:
1) class="tags" href="/tags/C.html" title=c>color: #0000ff;">对于空结构体class="tags" href="/tags/C.html" title=c>c;sizeof == 1;因为必须保证结构体的每一个实例在内存中都有独一无二的地址.
2)class="tags" href="/tags/C.html" title=c>color: #0000ff;">结构体的静态成员不对结构体的大小产生影响class="tags" href="/tags/C.html" title=c>c;因为静态变量的存储位置与结构体的实例地址无关。例如:
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct {staticlass="tags" href="/tags/C.html" title=c>c int I;} T; class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct {class="tags" href="/tags/C.html" title=c>char a; staticlass="tags" href="/tags/C.html" title=c>c int I;} T1;
sizeof(T) == 1; sizeof(T1) == 1;
下面是CSDN上提出的问题(原文<>:http://class="tags" href="/tags/C.html" title=c>community.class="tags" href="/tags/C.html" title=c>csdn.net/Expert/Topiclass="tags" href="/tags/C.html" title=c>cView3.asp?id=3804035)
---------------------------------------
#pragma paclass="tags" href="/tags/C.html" title=c>ck(8)
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct s1{
short a;
long b;
};
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct s2{
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c;
s1 d;
long long e;
};
#pragma paclass="tags" href="/tags/C.html" title=c>ck()
问
1.sizeof(s2) = ?
2.s2的class="tags" href="/tags/C.html" title=c>c后面空了几个字节接着是d?
---------------------------------------
下面是redleaves(ID最吊的网友)给出的解释:
很详尽,很透彻,从论坛原文后头的对话可以看出redleaves对C/C++的标准理解很深刻,值得偶学习:)
#pragma paclass="tags" href="/tags/C.html" title=c>ck(8)
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct S1{
class="tags" href="/tags/C.html" title=c>char a;
long b;
};
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct S2 {
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c;
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct S1 d;
long long e;
};
#pragma paclass="tags" href="/tags/C.html" title=c>ck()
sizeof(S2)结果为24.
成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.
也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.
S1中,成员a是1字节默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;
S2 中,class="tags" href="/tags/C.html" title=c>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,
class="tags" href="/tags/C.html" title=c>c S1.a S1.b d
S2的内存布局:1***,11**,1111,****11111111
这里有三点很重要:
1.每个成员分别按自己的方式对齐,并能最小化长度
2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度
3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐
对于数组,比如:
class="tags" href="/tags/C.html" title=c>char a[3];这种,它的对齐方式和分别写3个class="tags" href="/tags/C.html" title=c>char是一样的.也就是说它还是按1个字节对齐.
如果写: typedef class="tags" href="/tags/C.html" title=c>char Array3[3];
Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度.
不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个.
如下一段代码:
#pragma paclass="tags" href="/tags/C.html" title=c>ck(4)
class="tags" href="/tags/C.html" title=c>class TestB
{
publiclass="tags" href="/tags/C.html" title=c>c:
int aa;
class="tags" href="/tags/C.html" title=c>char a;
short b;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c;
};
int nSize = sizeof(TestB);
这里nSize结果为12class="tags" href="/tags/C.html" title=c>c;在预料之中。
现在去掉第一个成员变量为如下代码:
#pragma paclass="tags" href="/tags/C.html" title=c>ck(4)
class="tags" href="/tags/C.html" title=c>class TestC
{
publiclass="tags" href="/tags/C.html" title=c>c:
class="tags" href="/tags/C.html" title=c>char a;
short b;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c;
};
int nSize = sizeof(TestC);
按照正常的填充方式nSize的结果应该是8class="tags" href="/tags/C.html" title=c>c;为什么结果显示nSize为6呢?
事实上class="tags" href="/tags/C.html" title=c>c;很多人对#pragma paclass="tags" href="/tags/C.html" title=c>ck的理解是错误的。
#pragma paclass="tags" href="/tags/C.html" title=c>ck规定的对齐长度class="tags" href="/tags/C.html" title=c>c;实际使用的规则是:
结构class="tags" href="/tags/C.html" title=c>c;联合class="tags" href="/tags/C.html" title=c>c;或者类的数据成员class="tags" href="/tags/C.html" title=c>c;第一个放在偏移为0的地方class="tags" href="/tags/C.html" title=c>c;以后每个数据成员的对齐class="tags" href="/tags/C.html" title=c>c;按照#pragma paclass="tags" href="/tags/C.html" title=c>ck指定的数值和这个数据成员自身长度中class="tags" href="/tags/C.html" title=c>c;比较小的那个进行。
也就是说class="tags" href="/tags/C.html" title=c>c;当#pragma paclass="tags" href="/tags/C.html" title=c>ck的值等于或超过所有数据成员长度的时候class="tags" href="/tags/C.html" title=c>c;这个值的大小将不产生任何效果。
而结构整体的对齐class="tags" href="/tags/C.html" title=c>c;则按照结构体中最大的数据成员和 #pragma paclass="tags" href="/tags/C.html" title=c>ck指定值 之间class="tags" href="/tags/C.html" title=c>c;较小的那个进行。
具体解释
#pragma paclass="tags" href="/tags/C.html" title=c>ck(4)
class="tags" href="/tags/C.html" title=c>class TestB
{
publiclass="tags" href="/tags/C.html" title=c>c:
int aa; //第一个成员class="tags" href="/tags/C.html" title=c>c;放在[0,3]偏移的位置class="tags" href="/tags/C.html" title=c>c;
class="tags" href="/tags/C.html" title=c>char a; //第二个成员class="tags" href="/tags/C.html" title=c>c;自身长为1class="tags" href="/tags/C.html" title=c>c;#pragma paclass="tags" href="/tags/C.html" title=c>ck(4),取小值class="tags" href="/tags/C.html" title=c>c;也就是1class="tags" href="/tags/C.html" title=c>c;所以这个成员按一字节对齐class="tags" href="/tags/C.html" title=c>c;放在偏移[4]的位置。
short b; //第三个成员class="tags" href="/tags/C.html" title=c>c;自身长2class="tags" href="/tags/C.html" title=c>c;#pragma paclass="tags" href="/tags/C.html" title=c>ck(4)class="tags" href="/tags/C.html" title=c>c;取2class="tags" href="/tags/C.html" title=c>c;按2字节对齐class="tags" href="/tags/C.html" title=c>c;所以放在偏移[6,7]的位置。
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c; //第四个class="tags" href="/tags/C.html" title=c>c;自身长为1class="tags" href="/tags/C.html" title=c>c;放在[8]的位置。
};
这个类实际占据的内存空间是9字节
class="tags" href="/tags/C.html" title=c>color: #0000ff;">类之间的对齐class="tags" href="/tags/C.html" title=c>c;是按照类内部最大的成员的长度class="tags" href="/tags/C.html" title=c>c;和#pragma paclass="tags" href="/tags/C.html" title=c>ck规定的值之中较小的一个对齐的。
所以这个例子中class="tags" href="/tags/C.html" title=c>c;类之间对齐的长度是min(sizeof(int),4)class="tags" href="/tags/C.html" title=c>c;也就是4。
9按照4字节圆整的结果是12class="tags" href="/tags/C.html" title=c>c;所以sizeof(TestB)是12。
如果
#pragma paclass="tags" href="/tags/C.html" title=c>ck(2)
class="tags" href="/tags/C.html" title=c>class TestB
{
publiclass="tags" href="/tags/C.html" title=c>c:
int aa; //第一个成员class="tags" href="/tags/C.html" title=c>c;放在[0,3]偏移的位置class="tags" href="/tags/C.html" title=c>c;
class="tags" href="/tags/C.html" title=c>char a; //第二个成员class="tags" href="/tags/C.html" title=c>c;自身长为1class="tags" href="/tags/C.html" title=c>c;#pragma paclass="tags" href="/tags/C.html" title=c>ck(4),取小值class="tags" href="/tags/C.html" title=c>c;也就是1class="tags" href="/tags/C.html" title=c>c;所以这个成员按一字节对齐class="tags" href="/tags/C.html" title=c>c;放在偏移[4]的位置。
short b; //第三个成员class="tags" href="/tags/C.html" title=c>c;自身长2class="tags" href="/tags/C.html" title=c>c;#pragma paclass="tags" href="/tags/C.html" title=c>ck(4)class="tags" href="/tags/C.html" title=c>c;取2class="tags" href="/tags/C.html" title=c>c;按2字节对齐class="tags" href="/tags/C.html" title=c>c;所以放在偏移[6,7]的位置。
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c; //第四个class="tags" href="/tags/C.html" title=c>c;自身长为1class="tags" href="/tags/C.html" title=c>c;放在[8]的位置。
};
//可以看出class="tags" href="/tags/C.html" title=c>c;上面的位置完全没有变化class="tags" href="/tags/C.html" title=c>c;只是类之间改为按2字节对齐class="tags" href="/tags/C.html" title=c>c;9按2圆整的结果是10。
//所以 sizeof(TestB)是10。
最后看原贴:
现在去掉第一个成员变量为如下代码:
#pragma paclass="tags" href="/tags/C.html" title=c>ck(4)
class="tags" href="/tags/C.html" title=c>class TestC
{
publiclass="tags" href="/tags/C.html" title=c>c:
class="tags" href="/tags/C.html" title=c>char a;//第一个成员class="tags" href="/tags/C.html" title=c>c;放在[0]偏移的位置class="tags" href="/tags/C.html" title=c>c;
short b;//第二个成员class="tags" href="/tags/C.html" title=c>c;自身长2class="tags" href="/tags/C.html" title=c>c;#pragma paclass="tags" href="/tags/C.html" title=c>ck(4)class="tags" href="/tags/C.html" title=c>c;取2class="tags" href="/tags/C.html" title=c>c;按2字节对齐class="tags" href="/tags/C.html" title=c>c;所以放在偏移[2,3]的位置。
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c;//第三个class="tags" href="/tags/C.html" title=c>c;自身长为1class="tags" href="/tags/C.html" title=c>c;放在[4]的位置。
};
//整个类的大小是5字节class="tags" href="/tags/C.html" title=c>c;按照min(sizeof(short),4)字节对齐class="tags" href="/tags/C.html" title=c>c;也就是2字节对齐class="tags" href="/tags/C.html" title=c>c;结果是6
//所以sizeof(TestC)是6。
感谢 class="tags" href="/tags/C.html" title=c>color: #1848b5;">Miclass="tags" href="/tags/C.html" title=c>chael 提出疑问class="tags" href="/tags/C.html" title=c>c;在此补充:
#pragma paclass="tags" href="/tags/C.html" title=c>ck
当数据定义中出现__declass="tags" href="/tags/C.html" title=c>clspeclass="tags" href="/tags/C.html" title=c>c( align() )时class="tags" href="/tags/C.html" title=c>c;指定类型的对齐长度还要用自身长度和这里指定的数值比较class="tags" href="/tags/C.html" title=c>c;然后取其中较大的。最终类/结构的对齐长度也需要和这个数值比较class="tags" href="/tags/C.html" title=c>c;然后取其中较大的。
可以这样理解class="tags" href="/tags/C.html" title=c>c; __declass="tags" href="/tags/C.html" title=c>clspeclass="tags" href="/tags/C.html" title=c>c( align() ) 和 #pragma paclass="tags" href="/tags/C.html" title=c>ck是一对兄弟class="tags" href="/tags/C.html" title=c>c;前者规定了对齐的最小值class="tags" href="/tags/C.html" title=c>c;后者规定了对齐的最大值class="tags" href="/tags/C.html" title=c>c;两者同时出现时class="tags" href="/tags/C.html" title=c>c;前者拥有更高的优先级。
__declass="tags" href="/tags/C.html" title=c>clspeclass="tags" href="/tags/C.html" title=c>c ( align() )的一个特点是class="tags" href="/tags/C.html" title=c>c;它仅仅规定了数据对齐的位置class="tags" href="/tags/C.html" title=c>c;而没有规定数据实际占用的内存长度class="tags" href="/tags/C.html" title=c>c;当指定的数据被放置在确定的位置之后class="tags" href="/tags/C.html" title=c>c;其后的数据填充仍然是按照#pragma paclass="tags" href="/tags/C.html" title=c>ck规定的方式填充的class="tags" href="/tags/C.html" title=c>c;这时候类/结构的实际大小和内存格局的规则是这样的:
在__declass="tags" href="/tags/C.html" title=c>clspeclass="tags" href="/tags/C.html" title=c>c( align () )之前class="tags" href="/tags/C.html" title=c>c;数据按照#pragma paclass="tags" href="/tags/C.html" title=c>ck规定的方式填充class="tags" href="/tags/C.html" title=c>c;如前所述。当遇到__declass="tags" href="/tags/C.html" title=c>clspeclass="tags" href="/tags/C.html" title=c>c( align() )的时候class="tags" href="/tags/C.html" title=c>c;首先寻找距离当前偏移向后最近的对齐点(满足对齐长度为 max(数据自身长度,指定值) )class="tags" href="/tags/C.html" title=c>c;然后把被指定的数据类型从这个点开始填充class="tags" href="/tags/C.html" title=c>c;其后的数据类型从它的后面开始class="tags" href="/tags/C.html" title=c>c;仍然按照#pragma paclass="tags" href="/tags/C.html" title=c>ck填充class="tags" href="/tags/C.html" title=c>c;直到遇到下一个__declass="tags" href="/tags/C.html" title=c>clspeclass="tags" href="/tags/C.html" title=c>c( align() )。
当所有数据填充完毕class="tags" href="/tags/C.html" title=c>c;把结构的整体对齐数值和__declass="tags" href="/tags/C.html" title=c>clspeclass="tags" href="/tags/C.html" title=c>c( align() )规定的值做比较class="tags" href="/tags/C.html" title=c>c;取其中较大的作为整个结构的对齐长度。
特别的class="tags" href="/tags/C.html" title=c>c;当__declass="tags" href="/tags/C.html" title=c>clspeclass="tags" href="/tags/C.html" title=c>c( align() )指定的数值比对应类型长度小的时候class="tags" href="/tags/C.html" title=c>c;这个指定不起作用。
class="tags" href="/tags/C.html" title=c>class="blogstory">
首先请大家先看下面代码:
typedef class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct
{
UINT32 NumElements;
union
{
UINT32 Objeclass="tags" href="/tags/C.html" title=c>ctHandle;
}Entry;
}STR_ARRAY, *PSTR_ARRAY;
还有这两句#pragma paclass="tags" href="/tags/C.html" title=c>ck(push, 1)
#pragma paclass="tags" href="/tags/C.html" title=c>ck(pop)
#pragma paclass="tags" href="/tags/C.html" title=c>ck( [ n ] )
该指令指定结构和联合成员的紧凑对齐。而一个完整的转换单元的结构和联合的紧凑对齐由/ Z p 选项设置。紧凑对齐用p a class="tags" href="/tags/C.html" title=c>c e 编译指示在数据说明层设置。该编译指示在其出现后的第一个结构或联合说明处生效。该编译指示对定义无效。当你使用#pragma paclass="tags" href="/tags/C.html" title=c>ck ( n ) 时, 这里n 为1 、2 、4 、8 或1 6 。第一个结构成员之后的每个结构成员都被存储在更小的成员类型或n 字节界限内。如果你使用无参量的#pragma paclass="tags" href="/tags/C.html" title=c>ck , 结构成员被紧凑为以/ Z p 指定的值。该缺省/ Z p 紧凑值为/ Z p 8 。
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器也支持以下增强型语法:
#pragma paclass="tags" href="/tags/C.html" title=c>ck( [ [ { p u s h | p o p } , ] [ 标识符, ] ] [ n] )若不同的组件使用p a class="tags" href="/tags/C.html" title=c>c k 编译指示指定不同的紧凑对齐, 这个语法允许你把程序组件组合为一个单独的转换单元。带p u s h 参量的p a class="tags" href="/tags/C.html" title=c>c k 编译指示的每次出现将当前的紧凑对齐存储到一个内部class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器堆栈中。编译指示的参量表从左到右读取。如果你使用p u s h , 则当前紧凑值被存储起来; 如果你给出一个n 的值, 该值将成为新的紧凑值。若你指定一个
标识符, 即你选定一个名称, 则该标识符将和这个新的的紧凑值联系起来。带一个p o p 参量的p a class="tags" href="/tags/C.html" title=c>c k 编译指示的每次出现都会检索内部class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器堆栈顶的值,并且使该值为新的紧凑对齐值。如果你使用p o p 参量且内部class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器堆栈是空的,则紧凑值为命令行给定的值, 并且将产生一个警告信息。若你使用p o p 且指定一
个n 的值, 该值将成为新的紧凑值。若你使用p o p 且指定一个标识符, 所有存储在堆栈中的值将从栈中删除, 直到找到一个匹配的标识符, 这个与标识符相关的紧凑值也从栈中移出, 并且这个仅在标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符, 将使用命令行设置的紧凑值, 并且将产生一个一级警告。缺省紧凑对齐为8 。p a class="tags" href="/tags/C.html" title=c>c k 编译指示的新的增强功能让你编写头文件, 确保在遇到该头文件的前后的紧凑值是一样的。
什么是内存对齐
考虑下面的结构:
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo
{
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c1;
short s;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c2;
int i;
};
假设这个结构的成员在内存中是紧凑排列的class="tags" href="/tags/C.html" title=c>c;假设class="tags" href="/tags/C.html" title=c>c1的地址是0class="tags" href="/tags/C.html" title=c>c;那么s的地址就应该是1class="tags" href="/tags/C.html" title=c>c;class="tags" href="/tags/C.html" title=c>c2的地址就是3class="tags" href="/tags/C.html" title=c>c;i的地址就是4。也就是
class="tags" href="/tags/C.html" title=c>c1 00000000, s 00000001, class="tags" href="/tags/C.html" title=c>c2 00000003, i 00000004。
可是class="tags" href="/tags/C.html" title=c>c;我们在Visual class="tags" href="/tags/C.html" title=c>c/class="tags" href="/tags/C.html" title=c>c++ 6中写一个简单的程序:
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo a;
printf("class="tags" href="/tags/C.html" title=c>c1 %p, s %p, class="tags" href="/tags/C.html" title=c>c2 %p, i %p/n",
(unsigned int)(void*)&a.class="tags" href="/tags/C.html" title=c>c1 - (unsigned int)(void*)&a,
(unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
(unsigned int)(void*)&a.class="tags" href="/tags/C.html" title=c>c2 - (unsigned int)(void*)&a,
(unsigned int)(void*)&a.i - (unsigned int)(void*)&a);
运行class="tags" href="/tags/C.html" title=c>c;输出:
class="tags" href="/tags/C.html" title=c>c1 00000000, s 00000002, class="tags" href="/tags/C.html" title=c>c2 00000004, i 00000008。
为什么会这样?这就是内存对齐而导致的问题。
为什么会有内存对齐
以下内容节选自《Intel Arclass="tags" href="/tags/C.html" title=c>chiteclass="tags" href="/tags/C.html" title=c>cture 32 Manual》。
字class="tags" href="/tags/C.html" title=c>c;双字class="tags" href="/tags/C.html" title=c>c;和四字在自然边界上不需要在内存中对齐。(对字class="tags" href="/tags/C.html" title=c>c;双字class="tags" href="/tags/C.html" title=c>c;和四字来说class="tags" href="/tags/C.html" title=c>c;自然边界分别是偶数地址class="tags" href="/tags/C.html" title=c>c;可以被4整除的地址class="tags" href="/tags/C.html" title=c>c;和可以被8整除的地址。)
无论如何class="tags" href="/tags/C.html" title=c>c;为了提高程序的性能class="tags" href="/tags/C.html" title=c>c;数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于class="tags" href="/tags/C.html" title=c>c;为了访问未对齐的内存class="tags" href="/tags/C.html" title=c>c;处理器需要作两次内存访问;然而class="tags" href="/tags/C.html" title=c>c;对齐的内存访问仅需要一次访问。
一个字或双字操作数跨越了4字节边界class="tags" href="/tags/C.html" title=c>c;或者一个四字操作数跨越了8字节边界class="tags" href="/tags/C.html" title=c>c;被认为是未对齐的class="tags" href="/tags/C.html" title=c>c;从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的class="tags" href="/tags/C.html" title=c>c;能够在一个总线周期中被访问。
某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐class="tags" href="/tags/C.html" title=c>c;这些指令将会产生一个通用保护异常(#GP)。双四字的自然边界是能够被16 整除的地址。其他的操作双四字的指令允许未对齐的访问(不会产生通用保护异常)class="tags" href="/tags/C.html" title=c>c;然而class="tags" href="/tags/C.html" title=c>c;需要额外的内存总线周期来访问内存中未对齐的数据。
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器对内存对齐的处理
缺省情况下class="tags" href="/tags/C.html" title=c>c;class="tags" href="/tags/C.html" title=c>c/class="tags" href="/tags/C.html" title=c>c++class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器默认将结构、栈中的成员数据进行内存对齐。因此class="tags" href="/tags/C.html" title=c>c;上面的程序输出就变成了:
class="tags" href="/tags/C.html" title=c>c1 00000000, s 00000002, class="tags" href="/tags/C.html" title=c>c2 00000004, i 00000008。
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器将未对齐的成员向后移class="tags" href="/tags/C.html" title=c>c;将每一个都成员对齐到自然边界上class="tags" href="/tags/C.html" title=c>c;从而也导致了整个结构的尺寸变大。尽管会牺牲一点空间(成员之间有空洞)class="tags" href="/tags/C.html" title=c>c;但提高了性能。
也正是这个原因class="tags" href="/tags/C.html" title=c>c;我们不可以断言sizeof(foo) == 8。在这个例子中class="tags" href="/tags/C.html" title=c>c;sizeof(foo) == 12。
如何避免内存对齐的影响
那么class="tags" href="/tags/C.html" title=c>c;能不能既达到提高性能的目的class="tags" href="/tags/C.html" title=c>c;又能节约一点空间呢?有一点小技巧可以使用。比如我们可以将上面的结构改成:
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct bar
{
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c1;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c2;
short s;
int i;
};
这样一来class="tags" href="/tags/C.html" title=c>c;每个成员都对齐在其自然边界上class="tags" href="/tags/C.html" title=c>c;从而避免了class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器自动对齐。在这个例子中class="tags" href="/tags/C.html" title=c>c;sizeof(bar) == 8。
这个技巧有一个重要的作用class="tags" href="/tags/C.html" title=c>c;尤其是这个结构作为API的一部分提供给第三方开发使用的时候。第三方开发者可能将class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/BianYiQi.html" title=编译器>编译器的默认对齐选项改变class="tags" href="/tags/C.html" title=c>c;从而造成这个结构在你的发行的DLL中使用某种对齐方式class="tags" href="/tags/C.html" title=c>c;而在第三方开发者哪里却使用另外一种对齐方式。这将会导致重大问题。
比如class="tags" href="/tags/C.html" title=c>c;foo结构class="tags" href="/tags/C.html" title=c>c;我们的DLL使用默认对齐选项class="tags" href="/tags/C.html" title=c>c;对齐为
class="tags" href="/tags/C.html" title=c>c1 00000000, s 00000002, class="tags" href="/tags/C.html" title=c>c2 00000004, i 00000008class="tags" href="/tags/C.html" title=c>c;同时sizeof(foo) == 12。
而第三方将对齐选项关闭class="tags" href="/tags/C.html" title=c>c;导致
class="tags" href="/tags/C.html" title=c>c1 00000000, s 00000001, class="tags" href="/tags/C.html" title=c>c2 00000003, i 00000004class="tags" href="/tags/C.html" title=c>c;同时sizeof(foo) == 8。
如何使用class="tags" href="/tags/C.html" title=c>c/class="tags" href="/tags/C.html" title=c>c++中的对齐选项
vclass="tags" href="/tags/C.html" title=c>c6中的编译选项有 /Zp[1|2|4|8|16] class="tags" href="/tags/C.html" title=c>c;/Zp1表示以1字节边界对齐class="tags" href="/tags/C.html" title=c>c;相应的class="tags" href="/tags/C.html" title=c>c;/Zpn表示以n字节边界对齐。n字节边界对齐的意思是说class="tags" href="/tags/C.html" title=c>c;一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上class="tags" href="/tags/C.html" title=c>c;取它们中的最小值。也就是:
min ( sizeof ( member ), n)
实际上class="tags" href="/tags/C.html" title=c>c;1字节边界对齐也就表示了结构成员之间没有空洞。
/Zpn选项是应用于整个工程的class="tags" href="/tags/C.html" title=c>c;影响所有的参与编译的结构。
要使用这个选项class="tags" href="/tags/C.html" title=c>c;可以在vclass="tags" href="/tags/C.html" title=c>c6中打开工程属性页class="tags" href="/tags/C.html" title=c>c;class="tags" href="/tags/C.html" title=c>c/class="tags" href="/tags/C.html" title=c>c++页class="tags" href="/tags/C.html" title=c>c;选择Code Generation分类class="tags" href="/tags/C.html" title=c>c;在Struclass="tags" href="/tags/C.html" title=c>ct member class="tags" href="/tags/ALIGNMENT.html" title=alignment>alignment可以选择。
要专门针对某些结构定义使用对齐选项class="tags" href="/tags/C.html" title=c>c;可以使用#pragma paclass="tags" href="/tags/C.html" title=c>ck编译指令。指令语法如下:
#pragma paclass="tags" href="/tags/C.html" title=c>ck( [ show ] | [ push | pop ] [, identifier ] , n )
意义和/Zpn选项相同。比如:
#pragma paclass="tags" href="/tags/C.html" title=c>ck(1)
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo_paclass="tags" href="/tags/C.html" title=c>ck
{
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c1;
short s;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c2;
int i;
};
#pragma paclass="tags" href="/tags/C.html" title=c>ck()
栈内存对齐
我们可以观察到class="tags" href="/tags/C.html" title=c>c;在vclass="tags" href="/tags/C.html" title=c>c6中栈的对齐方式不受结构成员对齐选项的影响。(本来就是两码事)。它总是保持对齐class="tags" href="/tags/C.html" title=c>c;而且对齐在4字节边界上。
验证代码
#inclass="tags" href="/tags/C.html" title=c>clude <stdio.h>
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo
{
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c1;
short s;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c2;
int i;
};
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct bar
{
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c1;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c2;
short s;
int i;
};
#pragma paclass="tags" href="/tags/C.html" title=c>ck(1)
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo_paclass="tags" href="/tags/C.html" title=c>ck
{
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c1;
short s;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c2;
int i;
};
#pragma paclass="tags" href="/tags/C.html" title=c>ck()
int main(int argclass="tags" href="/tags/C.html" title=c>c, class="tags" href="/tags/C.html" title=c>char* argv[])
{
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c1;
short s;
class="tags" href="/tags/C.html" title=c>char class="tags" href="/tags/C.html" title=c>c2;
int i;
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo a;
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct bar b;
class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo_paclass="tags" href="/tags/C.html" title=c>ck p;
printf("staclass="tags" href="/tags/C.html" title=c>ck class="tags" href="/tags/C.html" title=c>c1 %p, s %p, class="tags" href="/tags/C.html" title=c>c2 %p, i %p/n",
(unsigned int)(void*)&class="tags" href="/tags/C.html" title=c>c1 - (unsigned int)(void*)&i,
(unsigned int)(void*)&s - (unsigned int)(void*)&i,
(unsigned int)(void*)&class="tags" href="/tags/C.html" title=c>c2 - (unsigned int)(void*)&i,
(unsigned int)(void*)&i - (unsigned int)(void*)&i);
printf("class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo class="tags" href="/tags/C.html" title=c>c1 %p, s %p, class="tags" href="/tags/C.html" title=c>c2 %p, i %p/n",
(unsigned int)(void*)&a.class="tags" href="/tags/C.html" title=c>c1 - (unsigned int)(void*)&a,
(unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
(unsigned int)(void*)&a.class="tags" href="/tags/C.html" title=c>c2 - (unsigned int)(void*)&a,
(unsigned int)(void*)&a.i - (unsigned int)(void*)&a);
printf("class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct bar class="tags" href="/tags/C.html" title=c>c1 %p, class="tags" href="/tags/C.html" title=c>c2 %p, s %p, i %p/n",
(unsigned int)(void*)&b.class="tags" href="/tags/C.html" title=c>c1 - (unsigned int)(void*)&b,
(unsigned int)(void*)&b.class="tags" href="/tags/C.html" title=c>c2 - (unsigned int)(void*)&b,
(unsigned int)(void*)&b.s - (unsigned int)(void*)&b,
(unsigned int)(void*)&b.i - (unsigned int)(void*)&b);
printf("class="tags" href="/tags/C.html" title=c>class="tags" href="/tags/STRUCT.html" title=struclass="tags" href="/tags/C.html" title=c>ct>struclass="tags" href="/tags/C.html" title=c>ct foo_paclass="tags" href="/tags/C.html" title=c>ck class="tags" href="/tags/C.html" title=c>c1 %p, s %p, class="tags" href="/tags/C.html" title=c>c2 %p, i %p/n",
(unsigned int)(void*)&p.class="tags" href="/tags/C.html" title=c>c1 - (unsigned int)(void*)&p,
(unsigned int)(void*)&p.s - (unsigned int)(void*)&p,
(unsigned int)(void*)&p.class="tags" href="/tags/C.html" title=c>c2 - (unsigned int)(void*)&p,
(unsigned int)(void*)&p.i - (unsigned int)(void*)&p);
printf("sizeof foo is %d/n", sizeof(foo));
printf("sizeof bar is %d/n", sizeof(bar));
printf("sizeof foo_paclass="tags" href="/tags/C.html" title=c>ck is %d/n", sizeof(foo_paclass="tags" href="/tags/C.html" title=c>ck));
return 0;
}