一 结构体的内存对齐
struct s1
{
char c1;
int i;
char c2;
};
struct s2
{
char c1;
char c2;
int i;
};
int main()
{
printf("%d\n", sizeof(struct s1));
printf("%d\n", sizeof(struct s2));
return 0;
}
这是输出结果: 很明显不是我们认为的6,6。 那为什么会是这样的输出结果呢?这就要说到结构体的内存对齐了。 首先我们要知道offsetof这是一种宏,用来计算结构体成员相对于起始位置的偏移量的。 比如对于上述struct s1中的各成员: 其偏移量分别是 0,4,8.
然后就是结构体内存对齐有如下的几个规则: 1.结构体的第一个成员直接对齐到相对于结构体变量起始位置为0的偏移处。
2.从第二个成员开始,要对齐到某个对齐数的整数倍的偏移处 对齐数:结构体成员自身大小和默认对齐数的较小值 默认对齐数: vs:8 linux:结构体成员自身大小
3.结构体的总大小,必须是最大对齐数的整数倍。 每个结构体成员都有一个对齐数,其中最大的为最大对齐数
4.如果嵌套结构体,该嵌套结构体对齐到自己最大对齐数的整数倍处。 结构体的整体大小,是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
为了说明上述规则我们结合文章开头的问题进一步说明
对于输出的第一个: 对于输出的第二个:
再举一个含有嵌套结构体的例子说明:
二 位段
位段的声明和结构是类似的,有两个不同: 1.位段的成员必须是 int、unsigned int 或signed int 。 2.位段的成员名后边有一个冒号和一个数字。
struct A {
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
int main()
{
printf("%d", sizeof(struct A));
return 0;
}
对于这个程序它的输出结果为8,但是初学者会认为是16,相当于4个int的和。那为什么是8而不是16呢? 那就要先介绍一下位段的内存分配了,规则如下:
- 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
位段冒号后面的数字表示该成员变量所占bit位的大小。
为了说明上述规则我们结合问题进一步说明
对于当前程序首先会在内存中开辟一个int类型(开辟空间的初始数值默认为0),也就是4个字节的空间来存放各成员变量。
1.首先存放_a变量用掉了2个bit位,当前开辟的空间还剩下30个bit位
2.再存放_b变量用掉了5个bit位,当前开辟的空间还剩下25个bit位
3.再存放_c变量用掉了510个bit位,当前开辟的空间还剩下15个bit位
4.最后想要存放_d变量(30bit)时发现当前所开辟的空间不足了,这时内存会再开辟一个int类型的空间来存放_d变量,至于是将原来还剩下的15bit位的空间用完再存,还是从新开辟的空间从头开始存,这取决于编译器。因此位段不具有平台移植性 综上我们可以知道内存为了存放这四个位段成员变量共开辟了2个int类型,也就是8个字节的空间。因此是8不是16.
`
|