关于字节对齐的问题。

来源:百度文库 编辑:神马文学网 时间:2024/04/28 21:44:13
主  题:
作  者:yndfcd (YNDFCD)
等  级:
信 誉 值:100
所属论坛:C/C++ C++ 语言
问题点数:100
回复次数:16
发表时间:2005-8-8 16:48:53
先看程序:
#pragma pack(8)
struct s1{
short a;
long b;
};
struct s2{
char c;
s1 d;
__int64 e;
};
#pragma pack()
int main(int argc, char* argv[])
{
s2 a;
s2* pa = &a;
char* pc = &a.c;
s1* pd = &a.d;
__int64* pe = &a.e;
printf("sizeof( s2 ) = %d, sizeof( s1 ) = %d", sizeof( s2 ),sizeof( s1 ) );
printf( "\n0x%8x\n0x%8x\n0x%8x\n0x%8x\n", pa, pd, pc, pe );
return 0;
}
下面是输出:
sizeof( s2 ) = 24, sizeof( s1 ) = 8
0x 65fde0
0x 65fde0
0x 65fde4
0x 65fdf0
Press any key to continue
问题一:为什么s2::d会出现在0x65fde4的位置上(8字节对齐,理论上应该出现在0x65fde8)?
问题三:为什么s2::d会占用12个字节?
回复人:Diego1983(迭戈(主要就搞JAVA)) () 信誉:1002005-8-8 17:09:51得分:0
很麻烦啊,晕了
想问句,我想学MFC,从哪本书开始啊,
我看WINDOWS程序设计,有点迷糊,看不很懂啊
介绍下啊
Top
回复人:aiguozhou(想起) () 信誉:1002005-8-8 17:19:26得分:0
《精通MFC程序设计》
Top
回复人:dongpy(51-->ARM) () 信誉:1162005-8-8 17:20:52得分:30
问题一:为什么s2::d会出现在0x65fde4的位置上
======================================================
s2::d是4字节对齐的
问题三:为什么s2::d会占用12个字节?
==========================
s2::d占8字节
Top
回复人:dongpy(51-->ARM) () 信誉:1162005-8-8 17:23:29得分:0
struct s2变量内存布局:
c *** d **** e (*是1字节的补齐字节)
Top
回复人:Diego1983(迭戈(主要就搞JAVA)) () 信誉:1002005-8-8 17:25:14得分:0
听说还有本《深入浅出MFC》
不知道哪本更加好啊??
Top
回复人:LoveYouJustOneDay(哈哈) () 信誉:1002005-8-8 18:17:09得分:40
到处搜刮来的,整理了一下。
为了能使CPU对变量进行高效快速的访问,变量的起始地址应该具有某些特性,
即所谓的“对齐”。例如对于4字节的int类型变量,其起始地址应位于4字节边界上,
即起始地址能够被4整除。变量的对齐规则如下(32位系统):
Type
Alignment
char
在字节边界上对齐
short (16-bit)
在双字节边界上对齐
int and long (32-bit)
在4字节边界上对齐
float
在4字节边界上对齐
double
在8字节边界上对齐
structures
单独考虑结构体的个成员,它们在不同的字节边界上对齐。
其中最大的字节边界数就是该结构的字节边界数。
MSDN原话:Largest alignment requirement of any member
理解结构体的对齐方式有点挠头,如果结构体中有结构体成员,
那么这是一个递归的过程。
对齐方式影响结构体成员在结构体中的偏移设编译器设定的最大对齐字节边界数为n,
对于结构体中的某一成员item,它相对于结构首地址的实际字节对齐数目X应该满足
以下规则:
X = min(n, sizeof(item))
例如,对于结构体 struct {char a; int b} T;
当位于32位系统,n=8时:
a的偏移为0,
b的偏移为4,中间填充了3个字节, b的X为4;
当位于32位系统,n=2时:
a的偏移为0,
b的偏移为2,中间填充了1个字节,b的X为2;
结构体的sizeof
设结构体的最后一个成员为LastItem,其相对于结构体首地址的
偏移为offset(LastItem),其大小为sizeof(LastItem),结构体的字节对齐数为N,
则:结构体的sizeof 为: 若offset(LastItem)+ sizeof(LastItem)能够被N整除,
那么就是offset(LastItem)+ sizeof(LastItem),否则,在后面填充,
直到能够被N整除。
例如:32位系统,n=8,
结构体 struct {char a; char b;} T;
struct {char a; int b;} T1;
struct {char a; int b; char c;} T2;
sizeof(T) == 2; N = 1 没有填充
sizeof(T) == 8; N = 4 中间填充了3字节
sizeof(T2)==12; N = 4 中间,结尾各填充了3字节
注意:
1) 对于空结构体,sizeof == 1;因为必须保证结构体的每一个实例在内存中都
有独一无二的地址。
2) 结构体的静态成员不对结构体的大小产生影响,因为静态变量的存储位置与
结构体的实例地址无关。
例如:
struct {static int I;} T; struct {char a; static int I;} T1;
sizeof(T) == 1; sizeof(T1) == 1;
3) 某些编译器支持扩展指令设置变量或结构的对齐方式,如VC,
详见MSDN(alignment of structures)
以下为Linux内核代码中的例子:
__attribute__((regparm(0))) int printk(const char * fmt, ...)\
__attribute__ ((format (printf, 1, 2)));
禁止printk使用寄存器传递调用参数,并将printk的参数1作为printf格式串,
从参数2开始检查其类型;
void __switch_to(struct task_struct *prev, struct task_struct *next)\
__attribute__((regparm(3))) ;__switch_to保留3个寄存器用作传递参数;
void __attribute__ ((__section__ (".text.init"))) mem_init();
将mem_init编绎到.text.init段;
struct tasklet_head tasklet_vec[32 ]\
__attribute__((__aligned__((32)),__section__(".data.cacheline_aligned"))) ;
将tasklet_vec[32]编绎到.data.cacheline_aligned段,并将它在32字节边界上对齐;
void do_exit(long error_code)__attribute__((noreturn));do_exit不会返回;
struct Xgt_desc_struct { unsigned short size; unsigned long\
address __attribute__((packed));};将address在结构中紧凑排列。
Top
回复人:yndfcd(YNDFCD) () 信誉:1002005-8-8 19:38:35得分:0
多谢楼上的。
Top
回复人:yndfcd(YNDFCD) () 信誉:1002005-8-8 19:59:25得分:0
但是还是没有完全解决我的问题,看MSDN上的原话:
Every data object has an alignment-requirement. For structures, the requirement is the largest of its members. Every object is allocated an offset so that
offset % alignment-requirement == 0
每个数据对象都有一个alignment-requirement.对于结构体来说,Requirement 就是最大的成员。(似乎MSDN上这句话本身就是错的,到少应该是最大成员所占内存的大小)。每一个对象被放置在offset处,其中offset 满足 offset % alignment-requirement == 0;
按照上面的理论,s2的最大成员的大小为8,那么每个成员都应该被放置在 offset % 8 == 0的位置上。即0, 8 , 16 .....。(难道我的理解错了?或者是MSDN写错了)。
另外,如果我交换结构体s2中 d, 和 c的位置:
struct s3
{
s1 d;
char c;
__int64 e;
}
得到的c就位于相对offset为8的位置上,而e位于相对offset为16的位置。
Top
回复人:jiajun2001(嘉俊) () 信誉:1002005-8-8 20:49:48得分:0
并不是要求#pragma pack(8),就一定是每个成员都是8字节对齐
而是指一组成员要按照8字节对齐。
struct s1
{
short a; // 2字节
long b; // 4字节
};
整个s1小于8字节,因此s1就是8字节。
struct s2
{
char c; // 1字节
s1 d; // 8字节
__int64 e; // 8字节
};
整个s2小于12字节,但是由于#pragma pack(8)的限定,12不能与8字节对齐,因此s2就是24字节,c占用8字节
Top
回复人:defyer007(挑战者---在学MFC呢) () 信誉:1002005-8-8 23:39:00得分:0
学习...
我觉得应该还要注意它的起始地址
如果起始地址就不对齐的话,那可能后面需要填补更多的空字节,而造成大小的不同
Top
回复人:cnwhonker(龙神) () 信誉:1002005-8-9 8:09:55得分:0
孤独小胖,帮你顶一下,
Top
回复人:yndfcd(YNDFCD) () 信誉:1002005-8-9 8:18:54得分:0
jiajun2001(嘉俊) 你是不是看错了, c明显只占用了4字节,而d占用了12字节。如果c占用8字节我就不会来问了。
Top
回复人:yndfcd(YNDFCD) () 信誉:1002005-8-9 8:28:15得分:0
上面的程序可能写错了一点,修改一下:
printf( "\n0x%8x\n0x%8x\n0x%8x\n0x%8x\n", pa, pc, pd, pe );
输出:
sizeof( s2 ) = 24, sizeof( s1 ) = 8
0x 65fdd4
0x 65fdd4
0x 65fdd8
0x 65fde4
Press any key to continue
Top
回复人:ken0426(绅士亦花心) () 信誉:1002005-8-9 11:10:35得分:30
Search FAQ, please.
Top
回复人:weiym(浙江征服者联盟(CAZ)) () 信誉:1002005-8-9 13:06:06得分:0
mark
Top
回复人:yndfcd(YNDFCD) () 信誉:1002005-8-9 15:05:26得分:0
明白了,谢谢
Top
该问题已经结贴 ,得分记录: dongpy (30)、 LoveYouJustOneDay (40)、 ken0426 (30)、
_xyz