2026-08-20 结构体与动态内存管理
技术
C 语言
学习内容:C 语言 Day19 笔记——空类型指针、内存管理与结构体
空类型指针(void*)
void* 表示不确定基类型的指针,不能直接解引用。它可以代表一切其他类型的指针,其他指针与 void* 之间可自动转型(建议 void* 转具体类型时用强制转型,因为 C++ 编译器不支持自动转)。
"一切皆空,但空不是一切"——
void*能承接任意指针,但它自身不知道指向什么类型。C 语言通过void*实现泛型编程(GP, General Programming):与具体数据类型无关的编程处理方式。
内存管理深入
五区详细对比:
| 区域 | 容量 | 管理方式 | 初始化 | 存放内容 |
|---|---|---|---|---|
| 栈区(Stack) | 很小(若干兆,易栈溢出) | 系统自动 | 不初始化 | 形参、非静态局部变量 |
| 堆区(Heap) | 很大 | 程序亲自 | 不初始化 | 动态分配(malloc 等) |
| 静态存储区(Static) | 很大 | 系统自动 | 自动清零 | 静态局部变量、全局变量 |
| 常量区(Constant) | — | 系统自动 | — | 常量(只读) |
| 代码区(Code) | — | — | — | 指令(函数体) |
const 的存储位置:用
const修饰的非静态局部变量只是"名义上的常量"(不能通过名字写),仍存在栈区;而const修饰的静态局部变量和全局变量才是真正常量,存在常量区。
堆区内存管理库函数(stdlib.h):malloc、calloc、realloc、free。另积累:perror(打印错误信息)、exit(结束程序,参数即返回值)。
| 对比 | malloc | calloc |
|---|---|---|
| 参数 | 1 个(字节数) | 2 个(元素个数 + 元素宽度) |
| 初始化 | 不清零 | 自动清零 |
| 效率 | 更高 | 稍低 |
| 适用 | 一般场景 | 更适合堆区数组 |
realloc 调整堆区空间大小:扩大时若后面空间充足就就地扩大(返回值 = 原指针),否则申请更大的新空间、拷贝旧数据、释放旧空间(返回值 ≠ 原指针)。
内存操作库函数:memset(逐字节设值)、memcpy(内存拷贝,不处理重叠)、memmove(内存拷贝,处理重叠)、memcmp(内存比较,全同返回 0)。
经典面试题:
memcpy和memmove有啥区别?如何选择?——memcpy假定两块内存不重叠、效率更高;memmove能正确处理重叠区域(相当于"安全版"拷贝)。不确定是否重叠时用memmove。
结构体(Struct)
结构体:若干个不同类型(也可相同类型)数据构成的集合。构成结构体的数据称为数据成员(Data Member),也叫属性或字段(Field)。用于表示一件商品、一个学生、一本书等信息。
结构体是自定义数据类型,必须先声明类型、再定义变量。声明语法:
struct book
{
int isbn;
char name[31];
float price;
short type;
int cnt;
};
双向链表练习
今日自己练习了双向链表(doubly linked list):每个节点除 next 指向后继外,还多一个 prev 指向前驱。
- 节点结构:
struct Node { int val; struct Node *prev; struct Node *next; }; - 优点:可双向遍历;删除节点时无需像单向链表那样额外保存前驱指针;插入更灵活
- 代价:每节点多存一个指针,插入/删除要同时维护
prev、next两个指针
Rust
无。
思考
无。
明日计划
- C 语言:结构体深入(结构体指针、结构体数组、typedef 结构体)、链表进阶
- Rust:恢复学习节奏