目录
前言
结构体大小的计算
修改默认对齐数
前言
当我们了解结构体的声明,结构体的自引用,结构体变量的定义和初始化,如何计算结构体的大小呢?结构体类型的数据是在内存中如何存放的呢?这也是本文需要讨论的问题;
结构体大小的计算
//结构体声明
struct n
{
char c1;
int i;
char c2;
};
int main()
{
struct n n1;
//计算结构体的大小
printf("%d\n", sizeof(n1));
return 0;
}
char-字符类型-大小为1字节 int - 整型 - 大小为4字节,那么总大小是不是 1+4+1=6字节呢?
运行结果:
结果是12个字节,为什么?这就不得不涉及结构体内存对齐规则;
结构体内存对齐规则 :
1. 结构体第一个成员变量存放在与结构体变量偏移量为0的地址处;
2. 其他成员变量要对齐到对齐数的整数倍的地址处;
对齐数=编译器默认对齐数与成员变量大小的较小值;
vs默认对齐数为8字节,linux环境没有默认对齐数,对齐数就是成员自身的大小;
3. 结构体总大小为最大对齐数的整数倍;
4. 对于嵌套结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍;
接下来的讨论,全是在vs编译环境下,即默认对齐数为8; 假设创建结构体变量n1从0x0000ff40处开辟内存空间,示意图如下
根据结构体对齐第一条规则:结构体第一个成员变量存放在与结构体变量偏移量为0的地址处;
结构体成员变量c1就应该存放于偏移量为0的地址处,且占1byte; 如上图c1的位置;
根据结构体对齐第二条规则:其他成员变量要对齐到对齐数的整数倍的地址处;
1. 先计算对齐数 int在内存中占4个字节,默认对齐数为8个字节,对齐数为自身大小和默认对 齐数两者较小值 那么对齐数=4;结构体第二个成员变量就要对齐到4的整数倍,即偏移量等于4的位置,且占4byte,如上图i的位置;
2. 计算对齐数 har在内存中占1个字节,默认对齐数为8个字节,对齐数为自身大小和默认对 齐数两者较小值 那么对齐数=1;结构体第二个成员变量就要对齐到1的整数倍,即偏移量等于8的位置,且占1byte,如上图c2的位置;
根据结构体对齐第三条规则:结构体总大小为最大对齐数的整数倍;
第一个对齐数=4,第二个对齐数=1 最大对齐数=4;结构体总大小=4*j (j=1,2,....)
已经用掉9个字节,下一个4的整数倍即偏移量为11的位置,12个字节;如上图绿色方框所示;
综上,已经分析出结果为12字节,那分析步骤是否如同我们如上所述,需要进行进一步验证
offsetof() - 宏 头文件 #include<stddef.h>
返回值为unsigned int ,第一个参数为结构体变量类型,第二个参数为成员变量名;
计算结构体成员相对于起始位置的偏移量
验证如下:
# include <stddef.h>
struct n
{
char c1;
int i;
char c2;
};
int main()
{
printf("%u\n", offsetof(struct n, c1));
printf("%u\n", offsetof(struct n, i));
printf("%u\n", offsetof(struct n, c2));
return 0;
}
运行结果:
验证结果如上图所示,与示意图完美匹配,可以得出分析步骤完全正确;
当我们计算嵌套结构体的大小,如下计算n的大小,结果又是如何?
struct s
{
double d;// 对齐数1=8;
char c;//对齐数2=1;
int i;//对齐数3=4;
};
//嵌套结构体最大对齐数=8字节;
//struct s的总大小经计算为16个字节
struct n
{
char c1;//对齐数4=1;
struct s n1;
double d;//对齐数5=8;
};
int main()
{
printf("%d\n", sizeof(struct n));
return 0;
}
根据结构体对齐的第四条规则:对于嵌套结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍;
不难得出结论,结果为32字节;
修改默认对齐数
使用 # pragma预处理指令来修改默认对齐数
# pragma pack(1)//设置默认对齐数为1;
struct n
{
char c1;
int i;
char c2;
};
# pragma pack()//取消设置的默认对齐数,还原为默认;
int main()
{
printf("%d\n", sizeof(struct n));
return 0;
}
运行结果:
联合体
联合体定义
C语言中,变量的定义是分配存储空间的过程。一般的,每个变量都具有其独有的储存空间,那么是否可以在同一块存储空间储存不同的数据类型呢?
联合体也叫共用体,C语言中联合体的关键字是union,这种类型定义的变量也包含一些列的成员变量,特征是这些成员共用同一块内存空间;
//定义联合类型的声明
union 联合名
{
成员列表;
};
验证过程:验证联合体成员是否共用同一块内存
union un
{
char c;
int i;
};
int main()
{
union un u;
printf("%p\n", &u);
printf("%p\n", &(u.c));
printf("%p\n", &(u.i));
return 0;
}
运行结果:
联合体大小的计算
联合体成员是共用同一块内存空间,一个联合变量的大小至少是最大成员的大小(因为联合体至少得有能力保存最大的成员变量);
//计算联合体的大小
union un
{
char c;
int i;
};
int main()
{
union un u;
printf("%d\n", sizeof(u));
return 0;
}
运行结果:
但是联合体的大小一定是最大的成员的大小吗?
union un
{
char arr[5];
int i;
};
int main()
{
union un u;
printf("%d\n", sizeof(u));
return 0;
}
运行结果:
结果是8个字节,成员变量最大为int类型,只占4个字节,所以联合体变量的大小不一定是最大成员的大小;
联合体大小的计算
- 联合体的大小至少是最大成员的大小;
- 当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍处;
共用体从0偏移量处开始共用同一块内存空间,c为字符数组,数组成员为char,char占1个字节,vs默认对齐数为8,所以对齐数1=1;i为整型数据,占4个字节,vs默认对齐数为8所以对齐数2=8;所以最大对齐数=4;此时内存使用5个字节,但是要对齐到最大对齐数的整数倍的位置,即相对于起始位置的第8个字节处,即偏移量为7的位置,所以占用内存的大小为8字节;