结构体内存分配
来源:互联网 发布:工口动作数据 编辑:程序博客网 时间:2024/05/01 15:08
关于内存对齐
一:
1.什么是内存对齐
假设我们同时声明两个变量:
char a;
short b;
用&(取地址符号)观察变量a,
b的地址的话,我们会发现(以16位CPU为例):
如果a的地址是0x0000,那么b的地址将会是0x0002或者是0x0004。
那么就出现这样一个问题:0x0001这个地址没有被使用,那它干什么去了?答案就是它确实没被使用。因为CPU每次都是从以2字节(16位CPU)或是4字节(32位CPU)的整数倍的内存地址中读进数据的。如果变量b的地址是0x0001的话,那么CPU就需要先从0x0000中读取一个short,取它的高8位放入b的低8位,然后再从0x0002中读取下一个short,取它的低8位放入b的高8位中,这样的话,为了获得b的值,CPU需要进行了两次读操作。
但是如果b的地址为0x0002,那么CPU只需一次读操作就可以获得b的值了。所以编译器为了优化代码,往往会根据变量的大小,将其指定到合适的位置,即称为内存对齐(对变量b做内存对齐,a、b之间的内存被浪费,a并未多占内存)。
2.结构体内存对齐规则
结构体所占用的内存与其成员在结构体中的声明顺序有关,其成员的内存对齐规则如下:
(1)每个成员分别按自己的对齐字节数和PPB(指定的对齐字节数,32位机默认为4)两个字节数最小的那个对齐,这样可以最小化长度。
(2)复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。
(3)结构体对齐后的长度必须是成员中最大的对齐参数(PPB)的整数倍,这样在处理数组时可以保证每一项都边界对齐。
(4)计算结构体的内存大小时,应该列出每个成员的偏移地址,则其长度=最后一个成员的偏移地址+最后一个成员数的长度+最后一个成员的调整参数(考虑PPB)。
枚举类型只为最宽的数据分配内存,在不同时候,用的是同一块内存;
类型 对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
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的大小。由于结构体的成员可以是复合类型,比如另外一个结构体,所以在寻找最宽基本类型成员时,应当包括复合类型成员的子成员,而不是把复合成员看成是一个整体。但在确定复合类型成员的偏移位置时则是将复合类型作为整体看待。
- 结构体内存分配
- 结构体内存分配
- 结构体内存分配
- 结构体内存分配问题
- 结构体内存分配问题
- 结构体内存分配解析
- 结构体内存分配总结
- 结构体内存分配问题
- 结构体内存分配问题
- 结构体内存分配问题(转)
- 结构体内存分配对齐策略
- 关于结构体内存分配及释放
- 结构体内存分配问题(转)
- 结构体内存分配问题(转)
- 共用体和结构体内存分配
- c语言结构体内存分配问题
- 结构体内存分配问题(转)
- 结构体内存分配问题(转)
- Windows Phone 8.1的网络编程之HttpWebRequest类
- 【学霸养成记】学计算机网络的同学…
- 天地过客
- 你的Android不好用,都是因为这几…
- 中国最美的100首情诗
- 结构体内存分配
- Java开发者值得关注的7款新工具
- 一个人,一辈子最重要的事,其实就…
- 即使不编程,你也应该学会HTML和CS…
- 这辈子,我想和你好好的
- Git的深入理解与GitHub托管服务的…
- 在吗?
- SQL的几种连接:内连接、左联接、…
- 你心里装着什么,你就会与什么结缘