结构体的构成

首先位于结构体中的元素在计算机也是对应存储的。而结构体的名字其实就可以看成是第一个变量的首地址。

例如:

struct node
{
int val;
char c;
int a[2];
}

在内存中是这样的
| val | c | a[0] | a[1]
p p+4 p+5 p+9

而变量在内存中的位置于定义时的位置是对应的

字节对齐

首先说明什么事字节对齐。它指的是变量在内存中的首地址是变量的长度的整数倍。

例如定义一个int型变量,那么这个变量首地址尽量要是4的整数倍。也就是地址最后两位一定为零,如果是long型,地址最后三位一定为零

为什么要字节对齐?

字节对齐可以加快访问数据的效率。原因是64位处理器数据总线是64位的,一次传过来8个字节的数据,所以如果我们是按8的整倍数进行存放,那么一定可以一次性取完,但是如果不是8的整倍数,例如int型首地址是6,那么就不可以一次性取完,要两次来取。而如果我们放在4这个位置,那么一定可以一次性取完。

此外例如首地址是2,这样也可以一次性取完,但是这样会造成空间浪费,因为现在不能再存int型了,可能会导致剩下4个字节都不能用。

对于x86系列,可以通过拼凑的方式把数据合起来。但是arm系列,如果没字节对齐,甚至会直接报错。

因此对于上面这个例子在c这个位置后面应该是p+8,为了让后面的整型保持对齐。而这多出的三个字节不会被使用(从此我们可以得出定义char型最好定义4个,不然多浪费)

此外,结构体的首地址的倍数由后面元素中占用空间最多的那个来决定。结尾的地址也要是占用空间最多的整倍数。结尾也要符合标准是为了便于用数组,因为数组的地址是连续的,所以每个的头和尾都要满足要求。

字节对齐还有利于结构体中元素的访问。我们之所以可以直接写a.data是因为每个元素在结构体中所占用的空间大小是一样的(考虑字节对齐),这样我们就可以像数组那样直接加上一个偏移量去访问。

提示,为了节省空间,把相同类型的元素放到一块写,

例如:

struct node
{
char a;
int b;
char c;
|
struct nodeb
{
int a;
char b;
char c;
}

可以看出,第一种写法为保证字节对齐需要多消耗6字节的空间,而第二种写法只需要消耗两字节的空间。