结构体(Struct)
定义
结构体是将不同类型的数据组合成一个单独的数据类型的方式。每个数据成员在结构体中都有自己的存储空间。
struct 结构体名 {
数据类型 成员1;
数据类型 成员2;
// 其他成员
};
特点
- 成员可以是不同的数据类型:结构体中的各个成员可以是不同的数据类型。
- 独立的存储空间:结构体中的每个成员都有自己的存储空间,它们是独立的。
- 成员访问:通过点运算符(
.
)来访问结构体的成员,如person.name
。 - 内存对齐:编译器通常会对结构体进行内存对齐,以提高内存访问效率。
内存对齐规则
1、结构体按照其最长成员大小对齐,意味着最终的大小必须是最长成员大小的整数倍;
2、结构体成员按照结构体成员声明先后次序依次存放,并且每个成员的首字节放置的位置必须能够整除成员的字节数;
3、如果结构体某个成员的字节数大于CPU的字节数,则最长按照CPU的字节数对齐;
4、用预处理命令#pragma pack(n) 可以强制编译器按照指定的n来对齐,合法的n的数值分别是1、2、4、8、16。
struct Example {
char a; // 1 字节
int b; // 4 字节
short c; // 2 字节
};
- 成员
a
:char
类型,大小为 1 字节。存放在偏移量 0(无须对齐)。 - 成员
b
:int
类型,大小为 4 字节。按对齐要求,需要在 4 字节边界上。由于a
占用了 1 字节,因此有 3 字节的填充,使得b
存放在偏移量 4。 - 成员
c
:short
类型,大小为 2 字节。按对齐要求,需要在 2 字节边界上。b
占用了 4 字节,因此c
存放在偏移量 8(无须额外填充)。
结构体的总大小为 10 字节。为了使整个结构体的大小是其最宽成员(int
的 4 字节)的整数倍,可能会在结构体末尾增加 2 字节的填充,使得最终大小为 12 字节。
可以通过编译器特定的指令来调整对齐方式,例如在 GCC 中使用 #pragma pack
或 __attribute__((packed))
,来改变结构体成员的对齐方式,通常用于与特定硬件或协议的兼容性。
#pragma pack(1) // 强制按 1 字节对齐
struct ExamplePacked {
char a;
int b;
short c;
};
#pragma pack() // 恢复默认对齐
刷新缓冲区
- 自动刷新:某些情况下,系统会自动刷新缓冲区,如程序正常退出、缓冲区满、遇到换行符(行缓冲)。
- 手动刷新:使用函数
fflush()
强制刷新缓冲区中的数据,使其立即输出。 - 可以通过
fflush()
函数手动刷新缓冲区,确保数据立即输出
链表
定义
链表(Linked List)是一种常见的数据结构,由一系列节点组成,这些节点通过指针相互连接。链表在动态数据存储和灵活数据管理方面具有优势
基本操作
- 节点(Node):链表的基本组成单位,每个节点包含数据部分和指向下一个节点的指针。
- 头节点(Head):链表的起始节点,通常用于标识链表的起点。
- 尾节点(Tail):链表的终止节点,其指针通常指向
NULL
(在单链表中)或头节点(在循环链表中)。 -
插入(Insertion):
- 在头部插入:修改新节点的指针指向当前头节点,然后更新头节点指针。
- 在尾部插入:遍历到链表末尾,在最后一个节点之后添加新节点。
- 在中间插入:找到插入位置的前一个节点,修改指针以插入新节点。
-
删除(Deletion):
- 删除头节点:更新头节点指针指向下一个节点,释放原头节点。
- 删除尾节点:遍历找到倒数第二个节点,更新其指针为
NULL
,释放原尾节点。 - 删除中间节点:修改前一个节点的指针指向删除节点的下一个节点,释放删除节点。
-
查找(Search):遍历链表,查找与给定值匹配的节点。
-
遍历(Traversal):从头节点开始,顺序访问每个节点。