KyrieChao

2026-08-20 结构体与动态内存管理

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

无。

思考

无。

明日计划

  1. C 语言:结构体深入(结构体指针、结构体数组、typedef 结构体)、链表进阶
  2. Rust:恢复学习节奏