今天分享一道经典面试题,就是结构体是如何对齐的,以及结构体占几个字节
我们先来看一个例子
typedef struct Test{
char a;
int b;
char c;
}Test_t;
大家猜猜这个结构体Test_t的内存是多少?
肯定有人说是6,两个char类型分别为1字节,int类型4字节,加起来正好是6
但是,结果是这样吗,我们来运行一下,结果为12
这是为什么呢?
这就是内存对齐导致的
什么是结构体内存对齐:
结构体不像数组,结构体中可以存放不同类型的数据,它的大小也不是简单的各个数据成员大小之和,限于读取内存的要求,而是每个成员在内存中的存储都要按照一定偏移量来存储,根据类型的不同,每个成员都要按照一定的对齐数进行对齐存储,最后整个结构体的大小也要按照一定的对齐数进行对齐。
结构体的对齐原则
1.第一个成员的首地址为0
2.其他结构体成员要对齐到某个数字(对齐数)的最小整数倍的地址处;
对齐数=编译器默认的一个对齐数 与 该成员大小的较小值
32位linux中默认为4
64位linux中默认为8
3.结构体的总大小,为其成员中所含最大类型的整数倍
结构体的对齐步骤:
1.结构体各成员对齐
2.结构体总体对齐
怎样计算结构体的大小
实例解析:
例一:此代码在64位linux下编写
typedef struct Test1{
char a;
int b;
short c;
}Test_t1;
int main(){
printf("%ld\n",sizeof(Test_t1));
return 0;
}
打印结果为12,让我们分析一下为什么结果为12
我们来看看上面所提到的结构体内存对齐规则:
a是char类型,占一个字节,第一个数据成员,放在结构体变量偏移量为0 的地址处
b是int类型,占4个字节,b的有效对齐值为4,对齐到4的整数倍地址,即地址偏移量为4处,在内存中存放的位置为4,5,6,7
c是short类型,占2个字节,b的有效对齐值为2.对齐到2的整数倍地址,即地址偏移量为8处.在内存中存放的位置为8,9
结构体整体的对齐数为所有成员的对齐数中最大的一个,对齐数为4
结构体整体大小,按照上面数据占据空间大小,计算的结构体大小10字节。
按照对齐规则,应该对齐到4的倍数,实际大小为12字节
例二:
typedef struct Test2{
char a;
short c;
int b;
}Test_t2;
int main(){
printf("%ld\n",sizeof(Test_t2));
return 0;
}
结构体Test_t1与结构体Test_t2的区别仅仅只是调换了一下各成员间的顺序,那它所占的内存还是刚才的值吗?
打印结果为8,让我们分析一下为什么是8
a是char类型,占一个字节,第一个数据成员,放在结构体变量偏移量为0 的地址处
c是short类型,占2个字节,b的有效对齐值为2,依次查看2的整数倍地址是否可以存放两个字节,将占有俩个字节的b存放在地址偏移量为2和3处
b是int类型,占4个字节,c的有效对齐值为4,对齐到4的整数倍地址,即地址偏移量为4处,在内存中存放的位置为4,5,6,7
结构体整体的对齐数为所有成员的对齐数中最大的一个,对齐数为4
结构体整体大小,按照上面数据占据空间大小,计算的结构体大小8字节。
按照对齐规则,应该对齐到4的倍数,实际大小为8字节
我们可以看到,以上两个结构体成员变量一模一样,可是当我们按照内存对齐规则来计算两个结构体的大小的时候,会发现两个结构体的大小不一样
可以见得,结构体成员变量的顺序不同,可能会造成内存不必要的损失。将占用空间小的成员尽量集中在一起,可以有效地避免内存不必要的浪费。
此时,关于结构体的大小,我们应该清楚了不少,接下来,我们继续来看道例题:
struct S1{
double d;
char c;
int i;
};
int main(){
printf("%d\n", sizeof(struct S1)); //16
return 0;
}
它的结果是多少呢?16
解析:
double类型占8个字节
char占了下一个字节
int的对齐数为4,所以空3个字节从12开始
这个结构体的最大对齐数为8
所以该结构体占2*8 = 16个字节
为什么要使用内存对齐:
平台移植性好:不是所有的硬件平台都能访问任意地址上的数据;某些硬件平台只能在某些地址访问某些特定类型的数据,否则抛出硬件异常,及遇到未对齐的边界直接就不进行读取数据了。
CPU处理效率高:为了访问未对齐的内存,处理器 需要作两次内存访问;而对齐的内存访问仅需要一次访问。
结构体内存对齐是拿空间换取时间的做法,提高效率
关于结构体内存的讲解到此为止