💙作者:阿润菜菜
📖专栏:一起学习C言
本文目录
在内存中观察
结构体内存对齐的规则:
为什么存在内存对齐?
编程中我们该如何设计结构体?
修改默认对齐数
相关笔试题
在内存中观察
首先我们来看一段代码:这里有两个结构体,只有成员顺序不一样
//1
struct S1
{
char c1;
int i;
char c2;
};
//2
struct S2
{
char c1;
char c2;
int i;
};
那么你们觉得S2大小是几个字节?S1又是多少?
作为C语言初学者一般是这样认为的:char 1字节 int 4字节 所以都是6字节
但实际上结果不是这样的,为什么呢?下面我们就来看看结构体内存对齐的知识:
如图,假设0为结构体S1在内存中的首地址,顺序123...为相对于首地址的偏移量(宏 offsetof 用来计算结构体成员相对于起始位置的偏移量)
通过offsetof可以计算出三个变量(c1,i,c2)各自对应的偏移量: 0 ,4 ,8
我们发现,在内存中红色的空间并没有被使用,而是都被浪费掉了,同样观察结构体S2成员变量排列情况:
通过以上,我们可以知道结构体成员变量在内存中并不是按照顺序排列的,而是按照一定的规则进行内存对齐排序的:
结构体内存对齐的规则:
- 结构体的第一个成员直接对齐到相对于结构体变量起始位置为0的地址处
- 从第二个成员开始,要对齐到某个【对齐数】的整数倍的偏移处
- 对齐数:结构体成员自身大小和默认对齐数的较小值(VS中默认的值为8,Linux环境默认不设对齐数)
- 结构体的总大小,必须是最大对齐数的整数倍。每个结构体成员都有一个对齐数,其中最大的对齐数就是最大对齐数
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
为什么存在内存对齐?
总体来说:结构体的内存对齐是拿空间来换取时间的做法。
1. 平台原因 ( 移植原因 ) :不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。2. 性能原因 :数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
编程中我们该如何设计结构体?
//不推荐
struct S1
{
char c1;
int i;
char c2;
};
//推荐
struct S2
{
char c1;
char c2;
int i;
};
虽然S1和S2类型的成员一模一样,但是S2所占的内存空间就少了一些
修改默认对齐数
在结构在对齐方式不合适的时候,我们可以利用#pragma 这个预处理指令更改默认对齐数:
#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
相关笔试题
1. 如下C程序,在64位处理器上运行后 sz 的值是什么( )
正确答案:B
要注意的是64位的情况下指针是占8个字节的,剩余详细了解结构体内存对齐的规则
2.百度笔试题: 写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明
#define STRUCT_OFFSET(id, element) ((unsigned long)(&(( struct id*)0)->element))
考察: offsetof 宏的实现因为是求偏移量 所以假设结构体的首地址是0开始,把他转换成结构体指针类型,在用 -> 取得他的成员,前面加了一个& 就是取得这个成员的 地址,最后在强制转换成unsigned long, 这样就得到了偏移量。
本节完