一、什么是结构体
与int,double等内置类型不同,结构体是C语言为用户提供的一种进行多种类型定义的集合体。一言蔽之:结构体是可以一个存放多种类型的对象的集合。 例子:
int a;
double b;
long c;
struct tag
{
int aa;
double bb;
long cc;
};
值得一提的是,结构体类型作为一种自定义类型并不会在创建时就完成相关的值的初始化操作,而是首先创建一个“模板”,如果需要使用该结构体类型时则需要将该结构体作为变量类型进行创建结构体变量,比如需要使用tag类型的结构体创建变量时
struct tag t1;
struct tag t2 = {1, 2.0, 255};
struct tag t1 ={ .aa = 1,
.bb = 2.0,
.cc = 255};
结构体内的变量的声明可以是任何类型,如:数组、指针、其他结构体变量等等。
struct Node
{
char name[15];
int age;
struct* Node next;
}typedef Node;
Node n1 = {"Li Ming",
15,
NULL};
二、结构体内存对齐
上述描述中以及了解到结构体是一种“超级数组”,结构体内可以存储各种类型的变量。但是问题也接踵而至,一个内置类型如int 类型我们通过sizeof操作可以知道int类型的大小为4个字节,double 类型为8个字节。那么一个结构体类型所占的存储空间又是多少呢?有人突发妙想,认为只需将我们认识的类型进行堆砌即可。但下述例子有力的回应了这种观点的错误。
struct tag1
{
int aa;
double bb;
long cc;
};
struct tag2
{
double bb;
int aa;
long cc;
};
struct tag1 与struct tag2 内部所声明的变量类型完全相同,只有内部顺序不同,依照上述观点,你可以得出tag1与tag2所占空间均为4+8+8=20字节的结论。但是我们分别使用sizeof对tag1和tag2进行操作就会发现:tag1所占空间为24个字节(编译器对齐数为8)、tag2所占空间为64个字节(编译器对齐数为8)说明上述观点是存在错误的。这是因为由于编译器移植等性能原因,结构体类型的存储方式并不能完全依照内置类型方式进行存储。存在结构体内存对齐的存储方式以便代码高效运行。
1、 结构体内存对齐规则
- 结构体的第一个成员永远放在结构体起始位置偏移量为0的位置
- 从第二个成员开始,结构体成员总是放在对齐数的整数倍处(对齐数=编译器默认对齐数和自身大小的较小值)
- 结构体的总大小必须是各个成员的对齐数中最大那个对齐数的整数倍
- 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍处
那么在上述的tag1 中计算规则应为4+(4)+8+8=24,其中括号中表示对齐占位字节
在tag2 中计算规则应为8+4+(40)+8+(4)=64, 可以看出即使结构体存放相同的变量类型,顺序不同,所占空间将会有显著差异 所以,一般情况下,我们在定义结构体时应使用占位空间从小到大的形式对变量进行定义。
2、为什么存在内存对齐?
- 平台原因:不是所有硬件平台上都能访问地址上的任意数据;某些硬件只能特定地读取数据
- 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存访问仅需要一次。
这就是结构体与它的内存对齐,希望这篇文章对你有所帮助!
|