首页 > C/C++/Linux > 结构体成员对齐

结构体成员对齐

2009-11-25 21:46 星期三    浏览: 1,130    绿 发表评论 阅读评论

本文所述情况为x86下的Linux环境下用GCC编译测试所得,具体是ubuntu8.04/x86/GCC4.2.4

结构体中的数据成员,往往因为系统结构的原因需要进行字节对齐,这就是所谓的填充(padding)。在32位系统下,为便于寻址,提升效率,就采取32位对齐策略,即存储指针的数据长度往往是4Bytes。因此,当编译器发现结构体成员中某个成员自身长度大于或等于4Bytes时,就会让该数据成员的偏移量为4的倍数,前面的数据size不够时,即需填充(padding)。若是该数据成员为2Bytes时,只需让其偏移为偶数即可,若成员为char类型,只有一个字节,则不需padding。第二,整个结构体的size需要为4的倍数,否则在结构体后面进行padding。

示例1:

struct A
{

char n[10];//10->12, 2Bytes padding

int p;//4,其偏移量需为4的倍数,故上面的变量需要填充2Bytes

long int pp;//4

unsigned int ppp;//4

char x;//1->4, 3Bytes padding,填充3Bytes,让后续成员偏移量为4的倍数

float y;//4,

double h;//8,偏移量为4的倍数即可,故上面的变量不需填充

}a;//35->40

printf(“char n:     %d n”,sizeof(a.n));

printf(“int p:       %d n”,sizeof(a.p));

printf(“long int pp: %d n”,sizeof(a.pp));

printf(“unsigned ppp:%d n”,sizeof(a.ppp));

printf(“char x:      %d n”,sizeof(a.x));

printf(“float y:     %d n”,sizeof(a.y));

printf(“double h:    %d n”,sizeof(a.h));

printf(“struct A:    %d nn”,sizeof(struct A));

执行结果:

char n:     10

int p:       4

long int pp: 4

unsigned ppp:4

char x:      1

float y:     4

double h:    8

struct A:    40

示例2:
struct B
{
    short  i;//2
    char   j;//1->2, 1Byte padding,下面的double变量需要4的倍数偏移量,故需填充
    double k;//8
    char   l;//1
    char   m;//1
    char   n;//1
    char   o;//1  此处不再需填充,因为总的size已经是4的倍数
}b;//15->16
    printf(“sizeof B::i is %dn”,sizeof(b.i));
    printf(“sizeof B::j is %dn”,sizeof(b.j));
    printf(“sizeof B::k is %dn”,sizeof(b.k));
    printf(“sizeof B::l is %dn”,sizeof(b.l));
    printf(“sizeof B::m is %dn”,sizeof(b.m));
    printf(“sizeof B::n is %dn”,sizeof(b.n));
    printf(“sizeof B::o is %dn”,sizeof(b.o));
    printf(“sizeof B is %dnn”,sizeof(struct B));
执行结果:
sizeof B::i is 2
sizeof B::j is 1
sizeof B::k is 8
sizeof B::l is 1
sizeof B::m is 1
sizeof B::n is 1
sizeof B::o is 1
sizeof B is 16
示例3:
struct B2
{
    short  i;//2
    char   j;//1->2, 1Byte padding
    double k;//8
    char   l;//1
    char   m;//1->3, 2Bytes padding m的偏移值为13,因为其为1字节长度,前面的正好又是char型
    int n;//4
    char   o;//1->4, 3Bytes padding
    double p;//8
    char   q;//1->4, 3Bytes padding 总size不是4的倍数,需填充
}b2;//27->36
    printf(“sizeof B2::i is %d, offset is %d n”,sizeof(b2.i),&(((struct B2*)(0))->i));
    printf(“sizeof B2::j is %d  offset is %d n”,sizeof(b2.j),&(((struct B2*)(0))->j));
    printf(“sizeof B2::k is %d, offset is %d n”,sizeof(b2.k),&(((struct B2*)(0))->k));
    printf(“sizeof B2::l is %d, offset is %d n”,sizeof(b2.l),&(((struct B2*)(0))->l));
    printf(“sizeof B2::m is %d, offset is %d n”,sizeof(b2.m),&(((struct B2*)(0))->m));
    printf(“sizeof B2::n is %d, offset is %d n”,sizeof(b2.n),&(((struct B2*)(0))->n));
    printf(“sizeof B2::o is %d, offset is %d n”,sizeof(b2.o),&(((struct B2*)(0))->o));
    printf(“sizeof B2::p is %d, offset is %d n”,sizeof(b2.p),&(((struct B2*)(0))->p));
    printf(“sizeof B2::q is %d, offset is %d n”,sizeof(b2.q),&(((struct B2*)(0))->q));
    printf(“sizeof B2 is %dnn”,sizeof(struct B2));
执行结果:
sizeof B2::i is 2, offset is 0
sizeof B2::j is 1  offset is 2
sizeof B2::k is 8, offset is 4
sizeof B2::l is 1, offset is 12
sizeof B2::m is 1, offset is 13
sizeof B2::n is 4, offset is 16
sizeof B2::o is 1, offset is 20
sizeof B2::p is 8, offset is 24
sizeof B2::q is 1, offset is 32
sizeof B2 is 36
示例4:
struct C1{
    char i;//1->2, 1Byte padding 下面的short型为2Bytes长度,故让其偏移量为偶数即可
    short j;//2
    double k;//8
    char m[0];//8, offset is 12 偏移值是12,与下面的l相同,未占内存。但其size为8
    long l;//4, offset is 12, The offsets of l and m are same, so char m[0] not allocate space.
    char* o;//4 指针数据占4Bytes长度
}c1;//20
    printf(“sizeof C1::i is %d, offset is %d n”,sizeof(c1.i),&(((struct C1*)(0))->i));
    printf(“sizeof C1::j is %d  offset is %d n”,sizeof(c1.j),&(((struct C1*)(0))->j));
    printf(“sizeof C1::k is %d, offset is %d n”,sizeof(c1.k),&(((struct C1*)(0))->k));
    printf(“sizeof C1::m is %d, offset is %d n”,sizeof(c1.k),&(((struct C1*)(0))->m));
    printf(“sizeof C1::l is %d, offset is %d n”,sizeof(c1.l),&(((struct C1*)(0))->l));
    printf(“sizeof C1::o is %d, offset is %d n”,sizeof(c1.o),&(((struct C1*)(0))->o));
    printf(“sizeof srtruc C1 is %dnn”,sizeof(struct C1));
执行结果:
sizeof C1::i is 1, offset is 0
sizeof C1::j is 2  offset is 2
sizeof C1::k is 8, offset is 4
sizeof C1::m is 8, offset is 12
sizeof C1::l is 4, offset is 12
sizeof C1::o is 4, offset is 16
sizeof srtruc C1 is 20
示例5:
struct F
{
    char i;//1Byte
    short j:2;//1Byte
    char k;//1Byte
    long l:1;//1Byte
    char m;//1Byte, 3Bytes padding 总共有5Bytes,需再填充3Bytes,让其成为4的倍数
}f;//5->8
    printf(“sizeof F::i is %d, offset is %d n”,sizeof(f.i),&(((struct F*)(0))->i));
    printf(“sizeof F::k is %d  offset is %d n”,sizeof(f.k),&(((struct F*)(0))->k));
    printf(“sizeof F::m is %d, offset is %d n”,sizeof(f.m),&(((struct F*)(0))->m));
    printf(“sizeof struct F is %dnn”,sizeof(struct F));//4
执行结果:
sizeof F::i is 1, offset is 0
sizeof F::k is 1  offset is 2
sizeof F::m is 1, offset is 4
sizeof struct F is 8
总结:从上可以看出:第一,为了进行对齐,往往需要对前面的数据成员进行padding;第二,为了总size的对齐,也需对结构体尾部进行padding。因此,当使用一个指针去操作结构体数据成员时是很危险的。如:为了使指针向下移动指向下一个成员,采用:当前成员地址p=上一个成员地址p+sizeof(当前成员)这种方式是非常危险的,就是因为padding后指针对齐问题。当然,可以利用gcc编译器紧凑(packed)这个attribute,不进行填充,但这往往给跨平台运行带来问题,如早期的ARM就不支持奇数地址,否则会造成segmentation fault的段错误。

本文链接地址: http://blog.redwolf-soft.com/?p=622

原创文章,版权©红狼博客所有, 转载随意,但请注明出处。

    分享到:

相关文章:

  • 无相关文章
分类: C/C++/Linux 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.
订阅评论
  欢迎参与讨论,请在这里发表您的看法、交流您的观点。