公司动态

C语言-指针、数组、结构体、动态内存分配复习记录

📅 2026/8/14 16:52:48
C语言-指针、数组、结构体、动态内存分配复习记录
目录一、指针Pointer1. 基础知识1. 本质2. 指针的运算3. 空指针与野指针4. 二级指针指向指针的指针2. 力扣对应习题二、数组Array1. 声明与初始化2. 数组名与指针的关系3. 数组作为函数参数4. 多维数组2. 力扣对应习题三、结构体Struct1. 定义与使用2. typedef 简化3. 结构体对齐与填充4. 结构体指针与动态分配5. 结构体嵌套与自引用2. 力扣对应习题四、动态内存分配1. 标准库函数2. 使用示例3. 常见错误与陷阱4. 动态分配二维数组五、综合示例学生管理系统片段六、易混淆点总结一、指针Pointer1. 基础知识1. 本质指针是一个变量其值是另一个变量的内存地址。通过指针可以间接访问和修改那个变量。int a 10; int *p a; // p 存放 a 的地址 printf(%d\n, *p); // 解引用输出 10 *p 20; // 等价于 a 202. 指针的运算取地址​获取变量的地址解引用​*访问指针指向的对象算术运算p n移动n * sizeof(类型)个字节常用于数组遍历指针相减得到两个指针之间的元素个数必须指向同一数组的元素int arr[] {1,2,3,4}; int *p arr; // p 指向 arr[0] int *q arr[2]; // q 指向 arr[2] printf(%ld\n, q - p); // 输出 23. 空指针与野指针NULLint *p NULL;表示指针不指向任何有效地址。解引用 NULL 会崩溃。野指针指针未被初始化或指向已释放的内存。必须避免因为解引用它行为未定义。int *p; // 野指针未初始化 *p 42; // 危险4. 二级指针指向指针的指针用于函数内部修改指针本身的值或动态二维数组。以下代码作用利用函数 alloc内部给外面的指针 p分配一块内存让 p指向这块内存。 void alloc(int **pp, int size) { *pp malloc(size * sizeof(int)); } int *p NULL; alloc(p, 10);为什么不能用一级指针​如果写成void alloc(int *p, int size)函数内部p malloc(...)只是修改了形参副本外面的p仍然是 NULL。所以需要传入指针的地址即二级指针才能在函数内部修改外面指针的值。一步步看int *p NULL;定义了一个整型指针p初始值为 NULL不指向任何地方。alloc(p, 10);把p的地址传给函数。p的类型是int **指向指针的指针。进入函数alloc(int **pp, int size)pp是一个二级指针它里面存的是p的地址。*pp就是解引用pp得到p本身因为pp指向p所以*pp就是p。*pp malloc(size * sizeof(int));等价于p malloc(40);假设 int 4 字节size10 则分配 40 字节。这样外面的p就被修改成了指向新分配的内存。malloc的作用​malloc是 C 标准库函数用于动态分配内存。它从堆区申请一块连续的内存空间返回该空间的起始地址void*类型。你需要把它转换成合适的指针类型。malloc(size * sizeof(int))表示申请size个int大小的空间比如10 * 4 40字节。如果分配成功返回指向这块内存的指针失败则返回NULL。总结​ 二级指针在这里充当了“能够修改外部指针”的桥梁。malloc负责在堆上开辟空间。2. 力扣对应习题206反转链表核心操作为修改节点间的next指针大量使用指针解引用和赋值141环形链表快慢指针技巧通过比较两个指针是否相遇判断环考验指针比较和移动。21合并两个有序链表使用两个指针分别遍历两个链表逐个比较并连接锻炼指针的遍历、赋值和判空。二、数组Array1. 声明与初始化int arr[5] {1,2,3}; // 剩余元素默认 0 int arr2[] {1,2,3,4,5}; // 自动推导长度为 5 int arr3[5] {0}; // 全部初始化为 02. 数组名与指针的关系数组名是首元素的地址常量不能赋值如arr other_arr非法但数组名可以看作int * const类型的指针下标运算arr[i]等价于*(arr i)int arr[3] {10,20,30}; int *p arr; // p 指向 arr[0] printf(%d %d\n, arr[1], *(p1)); // 20 203. 数组作为函数参数数组作为函数参数时会退化为指针因此需要额外传递长度。void print_array(int *arr, int len) { for (int i 0; i len; i) printf(%d , arr[i]); } // 调用print_array(arr, sizeof(arr)/sizeof(arr[0]));4. 多维数组int matrix[3][4]; // 3行4列 matrix[1][2] 5; // 访问第2行第3列本质上是一维数组的数组内存布局连续。传递给函数时需要指定第二维大小void func(int arr[][4], int rows)2. 力扣对应习题1两数之和基础数组遍历 下标访问可练习用数组模拟哈希表如固定大小桶体现数组的随机访问特性。27移除元素双指针法原地修改数组一个指针遍历一个指针记录新位置典型数组操作。189旋转数组数组元素整体移动可使用三次反转法依赖下标操作也可用临时数组加深对数组索引的理解。三、结构体Struct1. 定义与使用struct Student { char name[50]; int age; float score; }; struct Student stu1 {Alice, 20, 88.5}; stu1.age 21; // 直接访问成员 struct Student *p stu1; p-score 90.0; // 箭头运算符访问成员2. typedef 简化typedef struct { char name[50]; int age; } Person; Person p1 {Bob, 22};3. 结构体对齐与填充编译器会在结构体成员之间插入填充字节以满足对齐要求影响 sizeof 结果。struct Example { char c; // 1 字节 int i; // 4 字节从偏移量 4 开始对齐到 4 }; // sizeof 可能是 8而非 5可通过__attribute__((packed))取消对齐但性能下降。4. 结构体指针与动态分配struct Point { int x, y; }; struct Point *pp malloc(sizeof(struct Point)); pp-x 10; pp-y 20; free(pp);5. 结构体嵌套与自引用struct Node { int data; struct Node *next; // 自引用用于链表 };2. 力扣对应习题707设计链表自定义结构体 ListNode实现增删改查全面练习结构体定义、成员访问.和 -。94二叉树的中序遍历结构体 TreeNode的自引用左右孩子指针递归或迭代遍历理解结构体嵌套与指针组合。146LRU 缓存机制双向链表 哈希表结构体包含前驱、后继指针及数据字段是结构体复杂应用的经典题。四、动态内存分配1. 标准库函数函数原型作用mallocvoid *malloc(size_t size)分配 size 字节未初始化的内存callocvoid *calloc(size_t nmemb, size_t size)分配 nmemb*size 字节并清零reallocvoid *realloc(void *ptr, size_t new_size)调整之前分配的内存大小freevoid free(void *ptr)释放之前分配的内存2. 使用示例int *arr malloc(10 * sizeof(int)); // 分配 40 字节 if (arr NULL) { // 检查失败 fprintf(stderr, Memory allocation failed\n); exit(1); } for (int i 0; i 10; i) arr[i] i * 2; // 扩大数组 int *temp realloc(arr, 20 * sizeof(int)); if (temp ! NULL) { arr temp; } else { // realloc 失败原内存仍有效 } free(arr); // 释放3. 常见错误与陷阱内存泄漏分配后忘记free程序长期运行后耗尽内存。双重释放对同一指针调用两次free行为未定义。悬挂指针free后指针仍指向原地址再次使用读/写会导致未定义行为。应在释放后置为 NULL。越界访问写入超出分配大小的区域破坏堆管理结构。忘记检查 NULLmalloc失败返回 NULL解引用会崩溃。4. 动态分配二维数组// 方法1分配连续的二维数组推荐 int (*matrix)[COLS] malloc(ROWS * sizeof(*matrix)); // 使用matrix[row][col] // 方法2指针数组每行独立分配 int **matrix malloc(ROWS * sizeof(int *)); for (int i 0; i ROWS; i) matrix[i] malloc(COLS * sizeof(int)); // 释放时需逐行释放2. 力扣对应习题23合并 K 个升序链表需要动态创建新节点malloc或复用已有节点考验 malloc/free的正确使用及内存管理。138复制带随机指针的链表深度拷贝链表每个节点都需要 malloc新空间同时处理 random指针映射是动态分配的典型场景。622设计循环队列可使用动态数组malloc分配固定大小或链表实现涉及 realloc或手动管理缓冲区练习分配与释放。五、综合示例学生管理系统片段#include stdio.h #include stdlib.h #include string.h typedef struct { char name[50]; int id; float grade; } Student; Student *create_student(const char *name, int id, float grade) { Student *s malloc(sizeof(Student)); if (s NULL) return NULL; strncpy(s-name, name, 49); s-name[49] \0; s-id id; s-grade grade; return s; } void print_student(const Student *s) { printf(Name: %s, ID: %d, Grade: %.1f\n, s-name, s-id, s-grade); } int main() { Student *students[10]; students[0] create_student(Alice, 1001, 85.5); if (students[0]) print_student(students[0]); // 释放 for (int i 0; i 10; i) { free(students[i]); students[i] NULL; } return 0; }六、易混淆点总结概念说明int *p[5]指针数组包含 5 个int*元素的数组int (*p)[5]数组指针指向含有 5 个 int 的一维数组的指针sizeof(arr)数组名在sizeof中不退化返回整个数组字节数sizeof(p)指针变量的大小通常 8 字节在 64 位系统结构体赋值结构体可以直接赋值逐成员拷贝但含有指针时需深拷贝动态数组扩容使用realloc但注意原指针可能被移动