http://www.cnblogs.com/ddatsh/archive/2010/12/07/1899303.html
c中内存对齐
内存对齐问题要追溯到汇编时期
在C中如下结构:
struct
foo
{
char
c1;
short
s;
char
c2;
int
i;
};
假设结构成员在内存中紧凑排列,c1地址0,s地址应该是1,c2地址3,i地址4
c1 00000000
s 00000001
c2 00000003
i 00000004
但VC6 中
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->
#include
<
stdio.h
>
struct
foo
{
char
c1;
short
s;
char
c2;
int
i;
};
int
main()
{
struct
foo a;
printf(
"
c1\t%p\ns\t%p\nc2\t%p\ni\t%p\n
"
,
(unsigned
int
)(
void
*
)
&
a.c1
-
(unsigned
int
)(
void
*
)
&
a,
(unsigned
int
)(
void
*
)
&
a.s
-
(unsigned
int
)(
void
*
)
&
a,
(unsigned
int
)(
void
*
)
&
a.c2
-
(unsigned
int
)(
void
*
)
&
a,
(unsigned
int
)(
void
*
)
&
a.i
-
(unsigned
int
)(
void
*
)
&
a);
printf(
"
sizeof(foo)=%d\n
"
,
sizeof
(foo));
return
0
;
}
c1 00000000
s 00000002
c2 00000004
i 00000008
这就是内存对齐而导致的问题
为什么会有内存对齐
以下内容节选自《Intel Architecture 32 Manual》。
字,双字,和四字在自然边界上不需要在内存中对齐。(对字,双字,和四字来说,自然边界分别是偶数地址,可以被4整除的地址,和可以被8整除的地址。)
无论如何,为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问。
某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐,这些指令将会产生一个通用保护异常(#GP)。双四字的自然边界是能够被16整除的地址。其他的操作双四字的指令允许未对齐的访问(不会产生通用保护异常),然而,需要额外的内存总线周期来访问内存中未对齐的数据。
编译器对内存对齐的处理
默认将结构、栈中成员数据进行对齐
因此输出变成
c1 00000000
s 00000002
c2 00000004
i 00000008
编译器将未对齐的成员向后移,将每一个都成员对齐到自然边界上,导致整个结构尺寸变大。尽管会牺牲一点空间(成员之间有空洞),但提高了性能。
也正是这个原因,不可以断言sizeof(foo) == 8。在这个例子中,sizeof(foo) == 12。
避免内存对齐的影响
达到提高性能的目的,又节约一点空间
小技巧,改结构
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->
struct
bar
{
char
c1;
char
c2;
short
s;
int
i;
};
sizeof(bar) == 8
此结构作为API一部分给第三方开发使用时
第三方开发者可能改变默认对齐选项,而造成此结构在发行的DLL中使用某种对齐方式,而在第三方开发者那却使用另外一种对齐方式,导致重大问题
使用c/c++中对齐选项
vc6中编译选项有 /Zp[1|2|4|8|16|n] ,/Zp1 以1字节边界对齐
min ( sizeof ( member ), n)
实际上,1字节边界对齐也就表示了结构成员之间没有空洞
/Zpn用于整个工程,影响所有的参与编译的结构
vc6工程属性,c/c++,Code Generation,Struct member alignment
要专门针对某些结构定义使用对齐选项,可用
#pragma pack编译指令
#pragma pack( [ show ] | [ push | pop ] [, identifier ] , n )
意义和/Zpn选项相同
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->
#pragma
pack(1)
struct
foo_pack
{
char
c1;
short
s;
char
c2;
int
i;
};
#pragma
pack()
栈内存对齐
vc6中栈对齐方式不受结构成员对齐选项的影响。(本来就是两码事)。它总是保持对齐,而且对齐在4字节边界上
验证代码
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->
#include
<
stdio.h
>
struct
foo
{
char
c1;
short
s;
char
c2;
int
i;
};
struct
bar
{
char
c1;
char
c2;
short
s;
int
i;
};
#pragma
pack(1)
struct
foo_pack
{
char
c1;
short
s;
char
c2;
int
i;
};
#pragma
pack()
int
main(
int
argc,
char
*
argv[])
{
char
c1;
short
s;
char
c2;
int
i;
struct
foo a;
struct
bar b;
struct
foo_pack p;
printf(
"
stack c1 %p, s %p, c2 %p, i %p\n
"
,
(unsigned
int
)(
void
*
)
&
c1
-
(unsigned
int
)(
void
*
)
&
i,
(unsigned
int
)(
void
*
)
&
s
-
(unsigned
int
)(
void
*
)
&
i,
(unsigned
int
)(
void
*
)
&
c2
-
(unsigned
int
)(
void
*
)
&
i,
(unsigned
int
)(
void
*
)
&
i
-
(unsigned
int
)(
void
*
)
&
i);
printf(
"
struct foo c1 %p, s %p, c2 %p, i %p\n
"
,
(unsigned
int
)(
void
*
)
&
a.c1
-
(unsigned
int
)(
void
*
)
&
a,
(unsigned
int
)(
void
*
)
&
a.s
-
(unsigned
int
)(
void
*
)
&
a,
(unsigned
int
)(
void
*
)
&
a.c2
-
(unsigned
int
)(
void
*
)
&
a,
(unsigned
int
)(
void
*
)
&
a.i
-
(unsigned
int
)(
void
*
)
&
a);
printf(
"
struct bar c1 %p, c2 %p, s %p, i %p\n
"
,
(unsigned
int
)(
void
*
)
&
b.c1
-
(unsigned
int
)(
void
*
)
&
b,
(unsigned
int
)(
void
*
)
&
b.c2
-
(unsigned
int
)(
void
*
)
&
b,
(unsigned
int
)(
void
*
)
&
b.s
-
(unsigned
int
)(
void
*
)
&
b,
(unsigned
int
)(
void
*
)
&
b.i
-
(unsigned
int
)(
void
*
)
&
b);
printf(
"
struct foo_pack c1 %p, s %p, c2 %p, i %p\n
"
,
(unsigned
int
)(
void
*
)
&
p.c1
-
(unsigned
int
)(
void
*
)
&
p,
(unsigned
int
)(
void
*
)
&
p.s
-
(unsigned
int
)(
void
*
)
&
p,
(unsigned
int
)(
void
*
)
&
p.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_pack is %d\n
"
,
sizeof
(foo_pack));
return
0
;
}
stack c1 0000000B, s 00000008, c2 00000007, i 00000000
struct foo c1 00000000, s 00000002, c2 00000004, i 00000008
struct bar c1 00000000, c2 00000001, s 00000002, i 00000004
struct foo_pack c1 00000000, s 00000001, c2 00000003, i 00000004
sizeof foo is 12
sizeof bar is 8
sizeof foo_pack is 8
2347

被折叠的 条评论
为什么被折叠?



