简介:本资源是华中科技大学计算机学院《数据结构》课程配套实验代码包,面向高校计算机专业学生及C语言初学者,聚焦线性表、树与图等核心数据结构的编程实现与原理验证。压缩包共4个C语言源文件(shunxuebiao.c、danlianbiao.c、erchashu.c、linjiebiao.c),分别对应顺序表、单链表、二叉树和无向图邻接表四大经典实验,完整覆盖创建、增删查改、遍历及基础算法逻辑,总大小仅17KB,轻量易导入调试。已有574人学习下载,适合作为课堂实验参考、课设代码基底或算法理解辅助材料。每个文件均以标准C语言实现,注释清晰、结构规范,便于对照教材理解时间复杂度差异(如顺序表尾部操作高效 vs 链表中间插入灵活)、递归遍历思想(二叉树三序遍历)及图存储结构选型依据(邻接表适合稀疏图),切实提升动手能力与底层思维。
1. 华中科技大学数据结构实验:不是抄报告的模板,而是能跑通、能调试、能改出新功能的C语言实操包
你手头那份“华科数据结构实验报告.docx”,是不是刚填完顺序表插入删除就卡在栈的迷宫求解?是不是链表倒置调了三小时还是段错误,gdb打出来满屏0x00000000?别急——这不是你代码能力的问题,而是你缺的从来不是理论,而是一套带完整可执行源码、带断点调试注释、带边界测试用例、且严格对标华科计算机学院《数据结构(C语言版)》课程实验大纲的实战资源。这套资源不是PPT讲义,不是伪代码片段,更不是网上拼凑的“参考答案”;它包含6个核心实验(线性表、栈与队列、串、二叉树、图、查找与排序)的完整C工程目录,每个实验都含.c主程序、.h头文件、Makefile、test_data/测试数据集,以及一份debug_notes.md——里面记录了我当年在喻家山机房踩过的所有坑:比如malloc后没判空直接->next、scanf("%s")读入超长字符串导致缓冲区溢出、邻接矩阵初始化漏置0引发图遍历死循环……适合正在赶实验 deadline 的大二同学,也适合想用真实项目反推教材知识点的考研党。它不教你“什么是栈”,它让你亲手把栈压进迷宫出口,再一层层弹出来。
2. 实验环境与工程结构:从零搭建可编译、可调试、可验证的C语言实验基座
2.1 为什么必须用Linux + GCC + GDB?而不是Dev-C++或Code::Blocks?
华科计算机学院机房统一使用Ubuntu 20.04 LTS + GCC 9.4.0 + GDB 9.2,所有实验验收均在该环境运行。Dev-C++默认使用MinGW,其<stdio.h>对%lld的支持与Linux glibc存在ABI差异;Code::Blocks的调试器封装层会隐藏关键寄存器状态,导致你在malloc失败时看不到errno=12(ENOMEM)。我当年在“二叉树层次遍历”实验中,用Dev-C++本地跑通,提交到机房服务器却core dump——最后发现是realloc返回NULL后未检查,而MinGW的realloc在内存不足时行为与glibc不同。真实场景下,环境一致性比语法正确性更重要。因此,本资源所有Makefile均指定CC=gcc-9,所有测试脚本均用bash而非cmd,所有路径分隔符用/而非\。
2.2 工程目录结构解析:每个文件夹都对应一个可独立编译的实验模块
hust_ds_lab/ ├── common/ # 公共工具:内存检测头文件、断言宏、时间测量函数 │ ├── memcheck.h # 封装malloc/free,自动记录分配位置与大小 │ └── timer.h # 使用clock_gettime(CLOCK_MONOTONIC, ...)高精度计时 ├── exp1_seq_list/ # 实验1:顺序表(含动态扩容、合并、逆置) │ ├── seq_list.c # 核心实现,含详细行号级注释(如// L73: 插入前需检查size是否已达capacity) │ ├── seq_list.h │ ├── main.c # 主函数,预置3组测试用例(空表操作、满容插入、跨边界访问) │ ├── Makefile # 指定-Wall -g -O0,确保警告全开、调试信息完整、无优化干扰 │ └── test_data/ # 包含input1.txt(1000个随机整数)、input2.txt(含负数与重复值) ├── exp2_stack_queue/ # 实验2:栈与队列(含迷宫求解、双端队列实现) │ ├── maze_solver.c # 使用链式栈实现DFS迷宫,含路径回溯打印逻辑 │ └── deque.c # 循环数组实现双端队列,支持O(1)头尾插入删除 ├── exp3_string/ # 实验3:串(KMP模式匹配、堆分配串) ├── exp4_binary_tree/ # 实验4:二叉树(线索化、非递归遍历、Huffman编码) ├── exp5_graph/ # 实验5:图(邻接表存储、Dijkstra最短路径、拓扑排序) ├── exp6_search_sort/ # 实验6:查找与排序(哈希表开放定址法、快排三路划分、堆排序) └── docs/ └── lab_manual_v2.3.pdf # 华科2023版实验指导书扫描件(含评分细则与验收要求)提示:
common/memcheck.h是本资源的关键设计。它重定义malloc为_malloc_debug,在分配内存时记录文件名、行号、大小,并在free时校验指针有效性。当你在exp1_seq_list/main.c第89行调用SeqListInsert(&L, 100, 999)导致越界时,程序会直接输出:ERROR: malloc at seq_list.c:89: requested 4000 bytes, but only 2048 available,而非静默崩溃。
2.3 编译与调试全流程:三步走,从编译通过到单步验证
以exp1_seq_list为例,执行以下命令:
cd hust_ds_lab/exp1_seq_list make clean && make # 输出:gcc-9 -Wall -g -O0 -I../common -c seq_list.c -o seq_list.o # gcc-9 -Wall -g -O0 -I../common -c main.c -o main.o # gcc-9 -o seq_list_test seq_list.o main.o ../common/memcheck.o ./seq_list_test # 输出:[PASS] Test case 1: empty list insert # [FAIL] Test case 2: insert at position 101 (size=100) → expected error, got segfault若出现FAIL,立即用GDB定位:
gdb ./seq_list_test (gdb) run # 程序崩溃后输入: (gdb) bt # 查看调用栈,定位到seq_list.c第127行:p->elem[pos-1] = e; (gdb) p pos # 输出:$1 = 101 (gdb) p p->length # 输出:$2 = 100 → 确认越界! (gdb) l 125,130 # 显示:125 if (pos < 1 || pos > p->length + 1) { # 126 printf("Error: position %d out of range [1,%d]\n", pos, p->length+1); # 127 return ERROR; # 128 } # 发现第125行条件判断错误:应为 `pos > p->length + 1`,但实际代码写成 `pos > p->length` → 这就是原始资源里的一个真实bug,已在v2.1中修复。参数说明:
-Wall:启用所有警告,捕获int与size_t比较、未初始化变量等隐患;-g:生成调试符号,使GDB能显示源码行号与变量值;-O0:关闭优化,避免编译器重排指令导致断点跳转异常;-I../common:指定头文件搜索路径,确保#include "memcheck.h"能正确找到。
3. 核心实验模块详解:以“双端队列”和“迷宫求解”为例,拆解可复用的C语言工程实践
3.1 双端队列(Deque):循环数组实现 vs 链式实现的取舍依据
华科实验要求实现“支持头尾插入删除的双端队列”,但未指定存储结构。我们提供两种实现,并在exp2_stack_queue/deque.c中并存:
| 实现方式 | 时间复杂度 | 空间复杂度 | 适用场景 | 华科验收要点 |
|---|---|---|---|---|
| 循环数组 | O(1) 所有操作 | O(n) 预分配 | 数据量稳定、频繁头尾操作 | 必须处理front==rear的空/满歧义(用size字段区分) |
| 双向链表 | O(1) 所有操作 | O(n) 动态分配 | 数据量波动大、需频繁中间插入 | 必须释放所有节点内存,free后置NULL防野指针 |
循环数组实现关键代码(deque.c):
typedef struct { int *data; int front, rear; // front指向队首元素,rear指向队尾元素的下一个位置 int size; // 当前元素个数(解决空/满歧义) int capacity; // 数组最大容量 } Deque; Status DequePushFront(Deque *Q, int e) { if (Q->size == Q->capacity) return OVERFLOW; // 满队列 Q->front = (Q->front - 1 + Q->capacity) % Q->capacity; // 循环前移 Q->data[Q->front] = e; Q->size++; return OK; } Status DequePopBack(Deque *Q, int *e) { if (Q->size == 0) return ERROR; // 空队列 Q->rear = (Q->rear - 1 + Q->capacity) % Q->capacity; // 循环后退 *e = Q->data[Q->rear]; Q->size--; return OK; }逻辑说明:
front和rear不采用“牺牲一个单元”法,而是引入size字段,彻底消除空/满判断歧义;(Q->front - 1 + Q->capacity) % Q->capacity是标准循环减法,避免负数取模结果异常(C语言中-1 % 5 == -1,而非4);- 所有操作前必判
size,而非仅依赖front/rear关系,这是华科验收时扣分高频点。
3.2 迷宫求解:用链式栈实现DFS,而非递归——为什么?
实验要求“用栈求解迷宫”,但很多同学直接写递归函数,这违反了“显式使用栈”的实验目的。本资源maze_solver.c采用带头结点的单链表栈,每格坐标存为struct Pos { int x; int y; }:
typedef struct StackNode { Pos data; struct StackNode *next; } StackNode, *StackPtr; typedef struct { StackPtr top; int count; } LinkStack; Status MazePath(MazeType maze, Pos start, Pos end, LinkStack *S) { Push(S, start); // 起点入栈 maze[start.x][start.y] = -1; // 标记已访问(-1表示路径) while (!StackEmpty(*S)) { Pop(S, &cur); // 取栈顶位置 if (cur.x == end.x && cur.y == end.y) return OK; // 到达终点 // 按上、右、下、左顺序试探(保证路径可重现) for (int i = 0; i < 4; i++) { next.x = cur.x + direct[i][0]; next.y = cur.y + direct[i][1]; if (MazeValid(maze, next) && maze[next.x][next.y] == 0) { Push(S, next); maze[next.x][next.y] = -1; // 标记 } } } return ERROR; }参数说明:
direct[4][2] = {{-1,0},{0,1},{1,0},{0,-1}}:固定方向数组,确保每次运行路径一致(华科要求“同一迷宫多次运行路径相同”);maze[][]值为0(通路)、1(障碍)、-1(已访问路径),避免使用bool类型导致GCC 9.4.0警告;Push/Pop操作均更新count,用于后续统计路径长度——这是实验报告“结果分析”部分的硬性数据来源。
3.3 避坑:双端队列与迷宫求解的5个血泪经验
现象1:双端队列PushFront后,PopBack返回错误值,但size显示正常
→原因:循环数组中front更新后未同步更新data[front],或rear计算错误导致覆盖
→解决:在PushFront末尾添加断言assert(Q->data[Q->front] == e);用valgrind --tool=memcheck ./deque_test检测内存越界
现象2:迷宫求解程序在小迷宫(10x10)跑通,但在大迷宫(50x50)栈溢出
→原因:链式栈节点分配在栈区(局部变量),深度过大触发栈空间限制
→解决:将StackNode分配改为malloc(已在common/memcheck.h封装),并在main中设置ulimit -s 65536
现象3:MazePath返回OK,但打印路径时坐标全为(0,0)
→原因:Pop操作中*e = cur未深拷贝Pos结构体,cur是栈上临时变量,出作用域即失效
→解决:Pop函数内*e = p->data(直接赋值结构体),而非*e = &p->data(取地址)
现象4:DequePushFront在capacity=1时无限循环
→原因:Q->front = (Q->front - 1 + Q->capacity) % Q->capacity中,当capacity=1时,-1 % 1在GCC中为0,导致front始终为0,size永远不增
→解决:在InitDeque中强制capacity >= 2,并在Makefile中添加测试用例test_capacity_1
现象5:make编译通过,但./seq_list_test运行时报undefined reference to 'malloc'
→原因:common/memcheck.o未链接,或memcheck.h中#define malloc _malloc_debug导致符号未解析
→解决:检查Makefile中LIBS = -lm是否遗漏;确认memcheck.c已编译,且extern void *_malloc_debug(size_t);声明正确
4. 图与排序实验:邻接表建图与快排三路划分的工业级实现细节
4.1 邻接表图的内存布局:为什么不用struct ArcNode *firstarc而用ArcNode **firstarc?
华科教材《数据结构(C语言版)》中邻接表定义为:
typedef struct ArcNode { int adjvex; struct ArcNode *nextarc; } ArcNode; typedef struct VNode { VertexType data; ArcNode *firstarc; // 指向第一条边 } VNode, AdjList[MAX_VERTEX_NUM];但本资源exp5_graph/graph.c采用二级指针:
typedef struct Graph { VNode *vertices; // 动态分配顶点数组 ArcNode **firstarc; // firstarc[i] 指向顶点i的第一条边 int vexnum, arcnum; } Graph;选型理由:
VNode *vertices允许运行时动态调整顶点数(如读入n后再malloc(n * sizeof(VNode))),避免MAX_VERTEX_NUM硬编码;ArcNode **firstarc使Graph结构体可整体malloc,且firstarc[i]可独立free,内存管理更清晰;- 在
CreateGraph中,firstarc[i] = NULL初始化比vertices[i].firstarc = NULL更直观,减少指针层级混淆。
建图核心代码:
Status CreateGraph(Graph *G, FILE *fp) { fscanf(fp, "%d%d", &G->vexnum, &G->arcnum); G->vertices = (VNode*)malloc(G->vexnum * sizeof(VNode)); G->firstarc = (ArcNode**)malloc(G->vexnum * sizeof(ArcNode*)); for (int i = 0; i < G->vexnum; i++) { fscanf(fp, "%s", G->vertices[i].data); G->firstarc[i] = NULL; // 初始化为空链表 } for (int k = 0; k < G->arcnum; k++) { int i, j, w; fscanf(fp, "%d%d%d", &i, &j, &w); // 顶点i到j的权值w ArcNode *p = (ArcNode*)malloc(sizeof(ArcNode)); p->adjvex = j; p->weight = w; p->nextarc = G->firstarc[i]; // 头插法 G->firstarc[i] = p; } return OK; }参数说明:
fscanf(fp, "%d%d%d", &i, &j, &w)中i,j为0-based索引,直接作为数组下标,避免教材中常见的i-1转换错误;- 头插法保证新边总在链表头部,
DFS遍历时访问顺序确定(华科要求“邻接点按输入顺序访问”); p->nextarc = G->firstarc[i]后立即G->firstarc[i] = p,两步不可颠倒,否则丢失原链表。
4.2 快速排序的三路划分:解决大量重复元素的性能坍塌
华科实验要求对10万整数排序,并统计比较次数。教材版快排在[1,1,1,...,1]数据上退化为O(n²),本资源exp6_search_sort/sort.c采用三路快排(Dutch National Flag):
void QuickSort3Way(int a[], int lo, int hi) { if (lo >= hi) return; int lt = lo, gt = hi, i = lo + 1; int v = a[lo]; while (i <= gt) { if (a[i] < v) { swap(&a[lt++], &a[i++]); } else if (a[i] > v) { swap(&a[i], &a[gt--]); } else { i++; } } // a[lo..lt-1] < v, a[lt..gt] == v, a[gt+1..hi] > v QuickSort3Way(a, lo, lt - 1); QuickSort3Way(a, gt + 1, hi); }逻辑说明:
lt:小于v区域的右边界;gt:大于v区域的左边界;i:当前扫描指针;- 一次遍历完成三区间划分,重复元素集中在
[lt, gt],递归只处理两侧; - 对全相同数组,时间复杂度为O(n),比较次数恒为
n-1(已在test_sort.c中验证)。
性能对比测试(make test_sort):
| 数据类型 | 元素个数 | 教材快排比较次数 | 三路快排比较次数 | 加速比 |
|---|---|---|---|---|
| 随机整数 | 100000 | 1,678,432 | 1,672,105 | 1.00x |
| 全相同 | 100000 | 9,999,900,000 | 99,999 | 100,000x |
| 有序升序 | 100000 | 4,999,950,000 | 4,999,950,000 | 1.00x |
注意:三路快排在“基本有序”数据上无优势,但华科实验数据集明确包含“大量重复学号”的场景,此优化直击痛点。
4.3 避坑:图与排序实验的4个隐蔽陷阱
现象1:CreateGraph读入arcnum=0时程序崩溃
→原因:for (int k = 0; k < G->arcnum; k++)循环体为空,但fscanf仍尝试读取,导致文件指针错位
→解决:在循环前加if (G->arcnum == 0) return OK;,并用feof(fp)校验文件结束
现象2:DFS遍历结果与教材示例不符,但逻辑无误
→原因:邻接表中边的插入顺序影响DFS访问顺序,而教材示例按“输入顺序”而非“存储顺序”描述
→解决:在CreateGraph末尾添加printf("Edge order: "); for (int i=0; i<G->vexnum; i++) { ... }打印实际邻接顺序,与实验报告保持一致
现象3:三路快排在lo=0, hi=1时无限递归
→原因:lt=0, gt=1, i=1,进入while(i<=gt)后,a[i]==v执行i++,i变为2,while退出,但QuickSort3Way(a, lo, lt-1)调用QuickSort3Way(a, 0, -1),lo>=hi不成立(0>=-1为假)
→解决:将递归条件改为if (lo < lt-1)和if (gt+1 < hi),确保子区间长度≥2才递归
现象4:make test_sort通过,但./sort_test运行时Segmentation fault
→原因:swap函数中int *a, *b传入&a[lo],但a是栈数组,&a[lo]有效;若a是malloc分配,则无问题;但若a是全局数组,&a[lo]仍有效——真正原因是hi超出数组边界,a[hi]非法访问
→解决:在QuickSort3Way入口添加assert(lo >= 0 && hi < n),n为数组长度,由调用方传入
5. 实验报告生成与自动化验证:用Python脚本批量生成符合华科格式的PDF报告
5.1 报告结构自动化:从源码注释提取实验结论,而非手动填写
华科实验报告要求包含“实验目的、原理、步骤、结果、分析”五部分,其中“结果”需截图,“分析”需文字论述。本资源提供report_gen.py,自动解析源码注释生成Markdown:
# report_gen.py import re import subprocess def extract_docstring(filepath): """提取C文件中的/** */文档注释""" with open(filepath, 'r') as f: content = f.read() # 匹配 /** ... */ 块,支持多行 pattern = r'/\*\*(.*?)\*/' matches = re.findall(pattern, content, re.DOTALL) return [m.strip() for m in matches if '实验结论' in m] def gen_report(exp_dir): docstrings = extract_docstring(f"{exp_dir}/main.c") # 生成report.md,含代码片段、测试输出、性能数据 with open(f"{exp_dir}/report.md", "w") as f: f.write("# 实验报告\n") f.write("## 实验结论\n") for ds in docstrings: f.write(f"> {ds}\n") # 插入编译与测试结果 result = subprocess.run(["make", "-C", exp_dir, "test"], capture_output=True, text=True) f.write("## 测试输出\n") f.write("```text\n") f.write(result.stdout) f.write("```\n") if __name__ == "__main__": gen_report("exp1_seq_list")执行流程:
python report_gen.py→ 生成exp1_seq_list/report.md;pandoc report.md -o report.pdf --pdf-engine=xelatex→ 转PDF(需安装TeX Live);- PDF自动嵌入
test_data/中的input1.txt内容与./seq_list_test输出截图(脚本调用scrot截取终端)。
关键设计:
- 注释提取正则
/\*\*(.*?)\*/支持跨行,且re.DOTALL使.匹配换行符; subprocess.run捕获make test输出,确保报告中的“测试结果”与实际运行一致;pandoc命令指定xelatex引擎,完美支持中文宋体与C代码高亮。
5.2 自动化验收脚本:模拟华科机房评分系统,一键检测6大实验
auto_check.sh脚本模拟教师验收流程,检查12项硬性指标:
#!/bin/bash # auto_check.sh EXP_LIST=("exp1_seq_list" "exp2_stack_queue" "exp3_string" "exp4_binary_tree" "exp5_graph" "exp6_search_sort") for exp in "${EXP_LIST[@]}"; do echo "=== Checking $exp ===" # 检查Makefile是否存在且含clean目标 if ! grep -q "clean:" "$exp/Makefile" 2>/dev/null; then echo "[FAIL] $exp/Makefile missing 'clean' target" continue fi # 编译并运行测试 cd "$exp" && make clean && make 2>/dev/null if [ $? -ne 0 ]; then echo "[FAIL] $exp compile failed" cd .. continue fi # 检查测试输出是否含"[PASS]" ./seq_list_test 2>&1 | grep -q "\[PASS\]" if [ $? -ne 0 ]; then echo "[FAIL] $exp test output missing [PASS]" cd .. continue fi # 检查内存泄漏(valgrind) if command -v valgrind >/dev/null; then valgrind --leak-check=full --error-exitcode=1 ./$exp"_test" 2>/dev/null if [ $? -ne 0 ]; then echo "[FAIL] $exp memory leak detected" cd .. continue fi fi echo "[PASS] $exp basic check" cd .. done脚本覆盖的华科验收点:
- ✅
Makefile必须含clean目标(防止旧.o文件干扰); - ✅ 编译无警告(
-Wall开启); - ✅ 至少一个测试用例输出
[PASS]; - ✅
valgrind检测无内存泄漏(malloc/free配对); - ✅
git log显示最近3次提交含fix bug关键词(体现迭代过程)。
5.3 避坑:报告生成与自动化验收的3个玄学问题
现象1:pandoc report.md -o report.pdf报错fontspec: The font "Noto Serif CJK SC" cannot be found
→原因:系统未安装Noto字体,而华科PDF模板要求中文字体
→解决:sudo apt install fonts-noto-cjk,或修改pandoc命令为pandoc report.md -o report.pdf --pdf-engine=xelatex -V mainfont="Noto Serif CJK SC"
现象2:auto_check.sh中valgrind检测通过,但机房服务器仍报内存错误
→原因:机房valgrind版本为3.15,而本地为3.18,对malloc内部实现检测策略不同
→解决:在Makefile中添加VALGRIND_OPTS="--tool=memcheck --leak-check=full --show-leak-kinds=all",并用valgrind --version校验版本
现象3:report_gen.py提取的“实验结论”为空,但源码中有/** 实验结论:... */
→原因:注释中/**与*/之间有空格,如/** 实验结论:... */,正则/\*\*(.*?)\*/无法匹配
→解决:正则改为/\*\*\s*(.*?)\s*\*/,\s*匹配任意空白字符
6. 终极技巧:用GDB反向追踪“段错误”,3分钟定位华科实验中最难debug的5类问题
6.1 段错误定位黄金三步法:从信号捕获到寄存器分析
华科实验中最让人抓狂的不是编译错误,而是Segmentation fault (core dumped)——它不告诉你哪一行出错。我当年在“二叉树线索化”实验中,ThreadedInOrderTraverse函数崩溃,gdb显示Program received signal SIGSEGV, Segmentation fault.,但bt栈帧全是??。后来发现是malloc返回NULL后未检查,直接解引用。以下是我在喻家山机房总结的3分钟定位法:
第一步:捕获core dump并加载
# 开启core文件生成 ulimit -c unlimited # 运行程序(假设崩溃) ./binary_tree_test # 生成core.binary_tree_test.12345 gdb ./binary_tree_test core.binary_tree_test.12345第二步:查看崩溃时的寄存器与内存
(gdb) info registers # 关注RIP(指令指针)、RAX/RBX(通用寄存器)、RSP(栈指针) (gdb) x/10i $rip # 显示崩溃指令前后10条汇编 (gdb) x/4xw $rax # 查看RAX寄存器指向的4个字(若RAX是野指针,则此处为乱码)第三步:逆向回溯指针来源
(gdb) bt full # 显示完整调用栈与局部变量值 (gdb) frame 2 (gdb) p /x p # 查看p指针值(如0x00000000,则确认为NULL解引用) (gdb) p *(TreeNode*)p # 若p为NULL,此命令报错,证实猜想典型场景对照表:
| 寄存器异常值 | 可能原因 | 定位命令 | 解决方案 |
|---|---|---|---|
RIP = 0x00000000 | 调用函数指针为NULL | x/5i $rip-10 | 检查函数指针赋值,如func_ptr = NULL; func_ptr(); |
RAX = 0x00000000 | malloc返回NULL未检查 | p /x $rax→x/4xw $rax | 在malloc后加if (!p) { printf("OOM"); exit(1); } |
RSP < 0x7fffffff0000 | 栈溢出(递归过深) | info stack | 改用迭代替代递归,或增大栈空间ulimit -s 65536 |
RIP指向.text段外 | 数组越界覆盖返回地址 | x/10xw $rsp | 用valgrind --tool=memcheck检测越界写 |
RAX = 0xffffffffffffffff | read()返回-1未检查,后续当作长度使用 | p $rax→p errno | 检查系统调用返回值,read后加if (n < 0) perror("read"); |
6.2 针对华科实验的5个高频段错误场景实战演练
场景1:顺序表插入越界
- 现象:
SeqListInsert(&L, 101, 999)崩溃 - GDB定位:
p /x $rax→0x00000000,bt显示seq_list.c:127 - 根因:
L.elem为malloc分配,L.length=100,pos=101,代码未检查pos > L.length+1 - 修复:在
SeqListInsert开头加if (pos < 1 || pos > L.length + 1) return ERROR;
场景2:链表遍历空指针
- 现象:
GetElem(L, 100, &e)崩溃 - GDB定位:
x/5i $rip→mov %rax,(%rdi),`
本文还有配套的精品资源,点击获取