现在的位置: 首页 > 综合 > 正文

结构体的内存分配

2013年08月24日 ⁄ 综合 ⁄ 共 2285字 ⁄ 字号 评论关闭
假设这台机器 sizeof(char) = 1   sizeof(int) = 4    sizeof(double) = 8

枚举类型只为最宽的数据分配内存,在不同是时候,用的是同一块内存;

在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的
字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。
类型 对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char 偏移量必须为sizeof(char)即1的倍数
Short 偏移量必须为sizeof(short)即2的倍数
int 偏移量必须为sizeof(int)即4的倍数
float 偏移量必须为sizeof(float)即4的倍数
double  偏移量必须为sizeof(double)即8的倍数
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,
空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型
所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
结构体的内存分配依赖于结构成员的类型;
一:    struct s {
    int i;
    char c;
    double d;
    char* e;
    char a[];
};

首先来看对结构体 struct s {
    int i;
    char c;
};       的内存分配

    第1个成员偏移量为0,是4(int型大小)的整数倍。所以为其分配四个字节的空间,
    第2个成员 c 为char型,他的大小为1,首先假设在成员i和c之间没有填充字节,由于i是整型,占4个字节那么在没有填充之前,第2个成员c 相对于结构体的偏移量为 4,是1 ( char型的大小)的4倍,符合此条件,所以系统在给结构体第2个成员分配内存时,不会在i和c之间填充字节以到达对齐的目的。 所以在紧接着i 后为c 分配一个字节,现在结构体的大小应该是5 (4+1); 但是结构体要求:结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing
padding)。所以编译器会在后面填充3个字节, 因此该结构体的实际大小: sizeof(struct s) = 8;

再看结构体 struct s {
    int i;
    char c;
    double d;
};

    上面说到: 如果该结构体只包括i、c两个成员时,编译器会自动在最末一个成员之后加上3 填充字节,但是现在有了第3 个成员d, 该怎么分配呢? 根据上面的规则,由于成员c 相对于结构体的偏移量为 4,他1 ( char型的大小)的4倍, 所以在紧接着i 后为c 分配一个字节,现在结构体的大小应该是5 (4+1),那么成员d 的偏移量就应该是5,但是5 并不是8 (double 型的大小)的倍数,会在成员c 之后(或者说d 之前)填充3个字节,以使d 的偏移量到达8而成为4的整数倍。然后为d 分配8 个字节,因此现在struct
s 的大小就是16 (4 + 1 + 3 + 8);

   
同理,由于指针占4 个字节    所以
struct s {
    int i;
    char c;
    double d;
    char* e;
};   的大小为20

由于没有为数组a 指定大小,不为其分配空间, 因此struct s {
    int i;
    char c;
    double d;
    char* e;
    char a[];
};的大小为20;

二: struct s1{
            char a; //为a 分配1 个字节    
            struct s b; //20个字节
            char c; //1个字节
    };       的大小为28 = 1+3+20+1+3

字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:

1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

对于上面的准则,有几点需要说明:
1) 前面不是说结构体成员的地址是其大小的整数倍,怎么又说到偏移量了呢?因为有了第1点存在,所以我们就可以只考虑成员的偏移量,这样思考起来简单。想想为什么。结构体某个成员相对于结构体首地址的偏移量可以通过宏offsetof()来获得,这个宏也在stddef.h中定义,如下:
#define offsetof(s,m) (size_t)&(((s *)0)->m)
例如,想要获得S2中c的偏移量,方法为size_t pos = offsetof(S2, c);// pos等于4

2) 基本类型是指前面提到的像char、short、int、float、double这样的内置数据类型,这里所说的“数据宽度”就是指其sizeof的大小。由于结构体的成员可以是复合类型,比如另外一个结构体,所以在寻找最宽基本类型成员时,应当包括复合类型成员的子成员,而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。

更详细的解释:http://www.mttly.onlyblog.com/blog2/enchen/8744.html 里面主要是对sizeof的用法进行了总结,还说到了pack,

抱歉!评论已关闭.