在讨论这个问题之前我觉得先回顾一下结构体的各项性质。
1、结构体如何定义?
- struct A
{
int a;
int b;
};
A a1 = {1,2};
- struct B b1
{
.a = 1,
.b = 2
};
- struct C c1
{
a:1,
b:2
};
第一种比较常用,由它也可以衍生出不少初始化的方法:
struct A
{
int a;
int b;
}a1 = {1,2};
或者
struct
{
int a;
int b;
}a1 = {1,2};
在或者也可以直接使用typedef进行定义和更名。
在第二种和第三种初始化方法中成员的顺序可以改变,第一种是不可的。
2、为什么要使用结构体?
其实这个问题就要从数组的特点开始说起,因为数组拥有:相同类型的数据,连续分配的内存并且内存大小一旦确定就无法改变,数组的名字为一个指针,代表的是这个数组首元素的地址。
那么当我们要使用一个稍微复杂的数据集合体,比如学生这个个体,包含了学生的学号姓名班级成绩等信息,这样用一个数组是无法表述的,这个时候就引入了结构体类型,结构体类型可以定义一个包含这些所有数据类型的变量,从而实现对这个整体的操作。
3、接下来我们来谈一谈结构体的内存对齐问题。
现在有这样一个结构体类型:
struct A
{
int a;
char b;
double c;
};
如果按照常理来计算它的内存,应该是4+1+8=13B,但是用sizeof计算内存时,测得结果是16,这是怎么回事?我查了网上的资料了解到,结构体在分配内存的时候涉及到”内存对齐“的问题。那么什么是”内存对齐“呢?
这里我引用了百度百科对内存对齐的解释:内存对齐”应该是编译器的“管辖范围”。编译器为程序中的每个“数据单元”安排在适当的位置上。
具体的规则下面会有讨论,这里还要先说一下为什么要有内存对齐呢,像上面一样直接分配13个字节不就完了嘛?
大部分资料解释都是硬件原因,本人对硬件也不是很了解,比如说ARM单片机在取址的时候是32位取,也就是一次4个字节,如果不内存对齐,那么将会大大减小取址的效率,有的甚至直接抛出硬件异常,也就是所谓的平台移植问题。还有一点就是数据结构尤其是栈,应该最大限度在自然边界上对齐,不然处理器需要访问两次才能完成操作,效率受到影响。
那么如何内存对齐呢?
有三个规则:
1、结构体中的第一个成员的首地址也即是结构体变量的首地址。
2、结构体中的每一个成员的首地址相对于结构体的首地址的偏移量(offset)是该成员数据类型大小的整数倍。
3、结构体的总大小是对齐模数(对齐模数等于#pragma pack(n)所指定的n与结构体中最大数据类型的成员大小的最小值)的整数倍。
下面来举个例子:
struct A
{
short a; 4
int b; 4
char c; 4
double d; 8
};
在GCC下因为系统默认是4字节对齐,所以在我的电脑上运行后结果是20。
由于编译器会优化的问题,各个编译器可能不一样。