今天讲下结构体内存对其的问题:
因为结构体内存对齐问题算是面试或笔试时的常考问题,所以我选择单独开一篇来讲解一下!!!
这个问题我当时是有点懵逼的在看到的时候,不知道是怎么计算出来的。
终于经过一段时间我仔细看了计算规则和理解我算是弄明白了计算过程,下面我就来讲一下,有什么不对的也欢迎大家在下面评论指出!
1.结构体内存对齐是什么
结构体可以存储多种数据类型,这也就意味着结构体的大小不是简单的几个数据大小之和这样来计算,而是有一定的规则来对齐存储。因为限于内存的读取要求,这样效率会更高。
C / C ++中的每种数据类型都有对齐要求(事实上它是由处理器架构强制实现的,而不是由语言强制实施)。处理器将具有与数据总线大小相同的处理字长。在32位机器上 ,处理字大小为4个字节。
2.为什么要进行内存对其
原因1:
因为在32位操作系统(虽然64位操作系统,但是为了保证兼容性,编程仍然主要考量32位)中,数据总线是32位,地址总线是32位。
地址总线是32位,意味着寻址空间是按4递增的;数据总线32位意味着一次可读写4byte。
原因2:平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址 处取某些特定类型的数据,否则抛出硬件异常
下面来看个图为什么要内存对齐:
struct stu1
{
char a;
int b;
char c;
}
请看stu1这个结构体如果不内存对其的话,结果会是如下图这样:
对其前:
对其后:
没有内存对其的话也就意味着要执行stu1时需要读取两次内存,这样就浪费了时间,数据一多效率更是大大降低,如果对其的话,一次就可以了,众所周知,I/O操作是很耗时的,编译器做出对齐的选择也就好理解了。
结构体内存对齐规则
1 第一个成员在与结构体偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。注意:对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。 VS中默认的对齐数为8,gcc中的对齐数为4 .
3. 结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是 所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
在32位机器上,各个数据类型的自然对齐字节数:
类型 | 32位机器 |
---|---|
char | 1 |
short | 2 |
int | 4 |
float | 4 |
double | 8 |
long | 4 |
longlong | 4 |
下面有几个例子我们来看一下:
typedef struct St{ //sizeof(St)=4字节
char a;
char b;
short c;
} St;
typedef struct St1{ //sizeof(St1)=6字节
char a;
short c;
char b;
} St1;
typedef struct St2{ //sizeof(St2)=8字节
char a;
char b;
int c;
} St2
typedef struct St3{ //sizeof(St3)=12字节
char a;
int c;
char b;
} St3;
我以上面的St1和St3为例讲解一下计算方法:
St1:
首先char a在偏移量为0地址处,大小为1字节,此时偏移地址变到了1,short c大小2字节和vs默认对其数取最小值则是2,所以short c要放在偏移量为2的整数倍的地址处,那只能为2,所以前面的char a默认填充一字节,short c存储后,占2字节,此时偏移地址变到了4,char b大小1字节和vs默认对其数8取最小值为1,所以要存储在偏移地址为1的整数倍处,4就刚好可以,所以存放char b后偏移地址变为5,但此时结构体的整体大小要为该结构体中最大对其数的整数倍,该结构体中最大对其数为2,所以该结构体整体大小为6个字节。
St3:
首先char a存放在偏移地址为0的地方,大小1字节,偏移地址变为1,接下来存放int c,大小4字节和vs默认对齐数8取最小值为4,所以int c要存放在偏移地址为4的整数倍处,4就可以,所以char a默认填充3字节,偏移地址变为4,存放int c后,偏移地址变为8,char b为1字节和vs默认对齐数8取最小值为1,所以此时的8就刚好满足1的整数倍地址处,存放char b后偏移地址变为9,但结构体整体大小要为结构体中最大对其数4的整数倍,所以该结构体大小为12个字节。
最后再看一个结构体嵌套的情况:
typedef struct St4{
char d;
struct St5{
char a;
int b;
char c;
};
double e;
char f;
} St3;
St4里还嵌套了一个St5,这就需要利用我上面说的来做了:
嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的整数倍(含嵌套结构体的对齐数)的整数倍。
讲解:St5的对齐数是4,St5是12个字节,而St4结构体的最大对齐数是8(含St5来看了的),所以现在开始计算,首先char d在偏移地址0处1字节大小,然后偏移地址变为1,char a可以直接存储大小1字节,此时偏移地址变为2,int b大小4字节,必须放在偏移地址为4的整数倍的地方(因为4和vs默认对齐数8取最小值),偏移地址就变为4,存放int b后偏移地址变为8,8满足char c 1字节(因为1和vs默认对齐数取最小值为1),存放char c后偏移地址变为9,因为St5最大对其数为4,所以走完St5后偏移地址要变为4的整数倍,则为12,下来存储double e,8个字节,偏移地址要为8的整数倍(8和vs默认对齐数8取最小值),则偏移地址变为16,存储double e后偏移地址变为24,24满足char f 1个字节,所以存放char f后偏移地址变为25,结构体St4整体大小要为St4中最大对齐数(8)的整数倍,则为32字节大小。
如果上面的结果你都能算出来的话,说明你已经基本懂得了结构体内存对齐的计算方法。
以上过程我都写的很细致了,如果有什么不懂的请在下方评论留言,我会及时解答!