一、什么是结构体
1.1、结构体的概念
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。例如一个学生的信息就需要学号(字符串),姓名(字符串),年龄(整形)等等。
这些数据类型都不同但是他们又是表示一个整体,要存在联系,那么我们就需要一个新的数据类型。
——结构体,它就将不同类型的数据存放在一起,作为一个整体进行处理。
从这个例子可以明显看出结构体是怎么写的。
1.1.2、结构体含义
struct--定义结构体 。
Stu--结构体标签(方便了解结构体成员类型,在这里就是学生的相关信息)。
struct Stu:结构体类型。
1.1.3、结构体能否能够自引用呢?
当然是可以的,结构体也是能够自引用的,当然会有一些限制条件。
比如:定义一个链表的节点。
struct Node
{
int data;
struct Node* next; //切记这里Node需要加指针,否则结构体变量会变成无穷大
};
我们平时使用结构体时会经常用到 typedef 对结构体重命名,也容易引起问题
比如:为结构体定义一个新名字。
typedef struct Node //注意 定义结构体不要使⽤匿名结构体
{
int data;
struct Node* next;
}Node;
注意:在struct后面一点要加入结构体名字,否则无法使用typedef对结构体进行重命名。
1.2、结构体变量的创建和初始化
1.2.1、结构体类型如何定义结构体变量
可以看到,我们既能够在结构体末尾定义结构体变量,也能在外面定义结构体变量。
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
当然也可以定义结构体变量跟赋值同时操作。多类型结构体也是一样的操作,结构体镶嵌初始化也是如此,看下图。
切记一点,什么类型的结构体变量初始化就定义什么类型变量。而且一定要按照结构体成员位置顺序初始化。
1.2.2、如何得到结构体成员
两个操作符分别是:1 结构体变量 . 成员变量名2 结构体指针 ->成员变量名
在结构体传参时,因为参数时需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。
结论:结构体传参的时候,要传结构体的地址。
1.2.3、结构体对齐规则
1.2.4、为什么存在内存对齐
1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。2. 性能原因:数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。此内容采取文献,《 比特C语言 》
总体来说:结构体的内存对⻬是拿空间来换取时间的做法。
所以总的来说在设计结构体的时候,我们应当让空间小的成员尽量集中在一起,从而达到不浪费空间的目的。
//例如:
struct S1
{
char c1;
int i;
char c2;
};
//修改之后
struct S2
{
char c1; //两个char类型的成员放到一起
char c2;
int i;
};
从这里可以看到,S1和S2类型的成员一摸一样,但是S1和S2的所占空间大小是不一样的。尽量采取第二种写法。
1.2.5、结构体修改默认对齐数
在C语言中 #pragma 这个预处理指令,可以改变编译器的默认对⻬数。
#include <stdio.h>
#pragma pack(1) //设置默认对⻬数为1
struct S
{
char c1;
int i;
char c2;
};
#pragma pack() //取消设置的默认对⻬数,还原为默认
int main()
{
//输出的结果是什么?
printf("%d\n", sizeof(struct S));
return 0;
}
这个函数是用在结构体对齐方式不合适的时候,我们用来修改默认对齐数。
1.3、结构体实现位段
特别声明:以下皆是本人在比特学习课时借鉴的资料。
1.3.1 什么是位段
struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};
printf("%d\n", sizeof(struct A));
1.3.2 位段的内存分配
//⼀个例⼦
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
1.3.3 位段的跨平台问题
1.3.4 位段使⽤的注意事项
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
int main()
{
struct A sa = {0};
scanf("%d", &sa._b);//这是错误的
//正确的⽰范
int b = 0;
scanf("%d", &b);
sa._b = b;
return 0;
}