超详细带你走近结构体及结构体内存对齐!!
结构体的声明
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。结构用于表示一条记录,比如我们想要跟踪图书馆中某一本书的动态,我们就需要跟踪每本书的下列属性:
Title
Author
Subject
Book ID
结构的声明
而为了定义一个结构,我们则必须使用struct 语句。struct 语句定义了一个包含多个成员的数据类型。
struct tag {
member-list
member-list
member-list
...
} variable-list ;
Tag
是结构体标签。
Member-list
是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。
Variable-list
结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
那么我们就可以解决上述的一个问题了,就是如何描述一本书的信息。一本书的信息可以是书名、作者、科目以及书本ID等,而这些信息的类型都不相同,但我们仍然可以用Struct语句声明一本书book:
struct Books
{
char title[30];
char author[30];
char subject[60];
int book_id;
} book;
而使用struct语句时可以不完全声明。
匿名结构体类型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
在上面代码的基础上,p 与 &x是不相等的。第一个和第二声明被编译器当作两个完全不同的类型,即使他们的成员列表是一样的,如果令 p = &x,则是非法的。
结构的自引用
结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针,而通常这种指针的应用是为了实现一些更高级的数据结构如链表和树等。
//此结构体的声明包含了指向自己类型的指针
struct NODE
{
int data;
struct NODE* next;
};
//此结构体的声明方法是万万不可的,正确的自引用方式应该是上述方法。
struct Node
{
int data;
struct Node next;
};
//此结构体的声明包含了其他的结构体
struct COMPLEX
{
char string[100];
struct SIMPLE a;
};
结构体变量的定义和初始化
有了结构体类型,那如何定义变量,其实很简单。
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
结构体内存对齐
我们已经大致掌握了结构体的基本使用了,现在我们来一起深入的讨论一个问题,那就是如何计算结构体的大小?而这正涉及一个知识点:结构体的内存对齐。
我们可以先看一下下面一段代码。
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
int main()
{
printf("%d\n", sizeof(struct S3));
printf("%d\n", sizeof(struct S4));
如何求出结构体s3以及s4的大小呢?要解决这个问题,
首先得掌握结构体的对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
那么,我们则可以用这一个规则来求结构体的大小了,例如我们求s3的大小。第一步先看他的类型,分别是double(8字节)、char(1字节)以及int(4字节),那么他的结构体内存应当是:
那么我们就可以根据这个方法很好的求得结构体的大小了。
需要注意的是,0地址是任何数的倍数。大家可以类比一下参照物,0这个起始地址就是我们的一个参照物。
那么我们也可以很好的得到,s4的结构体大小为32字节
那么到这里为止,相信大家对结构体内存对齐已经有了一定的认识和理解了,但我们仍然不禁要问:为什么会存在内存对齐这种情况呢?
主要有以下几个原因:
- 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 - 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问
但归根结底,上面的说法我们都可以用一句话来概括:
结构体的内存对齐是拿空间来换取时间的做法。
那么,今天的结构体以及结构体对齐的相关内容我就讲述完啦,因为个人能力有限,文章难免会出现纰漏,届时有错误可以私信发给我以及时更正,谢谢大家!
祝大家新年快乐!