【数据结构学习 Day4】栈与队列核心概念梳理 + 顺序栈 / 链式栈 / 链式队列完整实现(附踩坑排错指南)
文章目录前言一、线性表、栈、队列的核心区别二、栈Stack核心概念1. 核心特性2. 基础术语3. 栈的分类4. 两种实现方式三、顺序栈完整实现空增栈1. 头文件定义 seqstack.h2. 接口实现 seqstack.c3. 【顺序栈易错踩坑点】四、链式栈完整实现带头结点1. 头文件定义 linkstack2. 接口实现 linkstack.c3. 【链式栈易错踩坑点】五、队列Queue核心概念1. 核心特性2. 基础术语3. 两种实现方式六、链式队列完整实现带头结点1. 接口说明2. 完整实现代码七、经典排错案例free(): double free detected in tcache 21. 报错含义2. 常见触发场景3. 避坑规范八、学习总结前言数据结构学习进入第四天今天的核心内容是操作受限的线性表 —— 栈和队列。相比于可以在任意位置插入删除的普通线性表栈和队列仅允许在指定端点进行操作是算法与工程开发中非常基础且高频使用的数据结构。本文整理了栈的核心概念、顺序栈与链式栈的完整代码实现、队列基础概念与链式队列实现同时汇总了今天实操中踩过的经典坑点方便复盘和后续查阅。一、线性表、栈、队列的核心区别普通线性表可在任意位置进行插入、删除操作操作自由度最高。栈和队列仅允许在指定位置进行插入删除属于「操作受限」的特殊线性表操作规则固定应用场景针对性强。二、栈Stack核心概念1. 核心特性先进后出FILO, First In Last Out/ 后进先出LIFO, Last In First Out最后存入的元素最先被取出。2. 基础术语栈顶允许进行入栈、出栈操作的一端是所有操作的唯一入口出口。栈底不允许进行插入删除操作的一端位置固定不变。入栈压栈将元素存入栈顶位置的操作。出栈弹栈将元素从栈顶位置取出的操作。栈针指向当前可入栈位置 / 栈顶元素的标识用于标记栈的当前状态。3. 栈的分类按栈针指向与内存增长方向区分空栈模型栈针指向下一个可存放元素的空位入栈时先存数据再挪动栈针。满栈模型栈针指向当前栈顶元素的位置入栈时先挪动栈针再存数据。增栈栈向内存高地址方向增长。减栈栈向内存低地址方向增长。本次代码实现采用空增栈模型也是最常用、最易理解的实现方式。4. 两种实现方式顺序栈底层基于数组实现内存连续访问效率高容量固定。链式栈底层基于单链表实现内存离散容量无上限伴随指针额外开销。三、顺序栈完整实现空增栈1. 头文件定义seqstack.h#ifndef SEQSTACK_H #define SEQSTACK_H typedef int DataType; typedef struct { DataType *pData; // 指向数据区首地址 int tLen; // 栈的最大容量 int Top; // 栈针指向下一个可入栈的位置 } Stack_t; Stack_t *CreateSeqStack(int Len); int IsEmptySeqStack(Stack_t *pTmpStack); int IsFullSeqStack(Stack_t *pTmpStack); int PushSeqStack(Stack_t *pTmpStack, DataType TmpData); DataType PopSeqStack(Stack_t *pTmpStack); int DestroySeqStack(Stack_t **ppTmpStack); #endif2. 接口实现seqstack.c#include stdio.h #include seqstack.h #include string.h #include stdlib.h // 创建顺序栈Len为最大容量 Stack_t *CreateSeqStack(int Len) { if (Len 0) { printf(栈容量必须大于0!\n); return NULL; } Stack_t *pTmpStack malloc(sizeof(Stack_t)); if (NULL pTmpStack) { printf(malloc stack head failed!\n); return NULL; } pTmpStack-pData malloc(Len * sizeof(DataType)); if (NULL pTmpStack-pData) { printf(malloc stack data failed!\n); free(pTmpStack); // 分配失败释放头结点防止内存泄漏 return NULL; } pTmpStack-tLen Len; pTmpStack-Top 0; memset(pTmpStack-pData, 0, Len * sizeof(DataType)); return pTmpStack; } // 判断栈空返回1为空0为非空 int IsEmptySeqStack(Stack_t *pTmpStack) { if (NULL pTmpStack) return -1; return pTmpStack-Top 0 ? 1 : 0; } // 判断栈满返回1为满0为未满 int IsFullSeqStack(Stack_t *pTmpStack) { if (NULL pTmpStack) return -1; return pTmpStack-Top pTmpStack-tLen ? 1 : 0; } // 入栈成功返回0失败返回-1 int PushSeqStack(Stack_t *pTmpStack, DataType TmpData) { if (NULL pTmpStack) return -1; if (IsFullSeqStack(pTmpStack)) { printf(栈满无法入栈\n); return -1; } pTmpStack-pData[pTmpStack-Top] TmpData; pTmpStack-Top; return 0; } // 出栈返回弹出的元素栈空返回0接口保持原设计 DataType PopSeqStack(Stack_t *pTmpStack) { if (NULL pTmpStack) { printf(栈指针为空\n); return 0; } if (IsEmptySeqStack(pTmpStack)) { printf(栈空不能出栈!\n); return 0; } pTmpStack-Top--; return pTmpStack-pData[pTmpStack-Top]; } // 销毁栈二级指针释放后置空 int DestroySeqStack(Stack_t **ppTmpStack) { if (NULL ppTmpStack || NULL *ppTmpStack) return -1; if ((*ppTmpStack)-pData ! NULL) { free((*ppTmpStack)-pData); (*ppTmpStack)-pData NULL; } free(*ppTmpStack); *ppTmpStack NULL; return 0; }3. 【顺序栈易错踩坑点】判空逻辑错误误用最大容量tLen判断空栈正确逻辑是判断Top 0。入栈缺少判满栈满后继续入栈会造成数组越界触发内存非法访问。出栈逻辑冗余多余的 for 循环完全无意义空栈出栈无返回值会触发未定义行为。内存泄漏创建栈时数据区分配失败未释放已分配的头结点。空指针未防护所有接口未判断入参是否为 NULL传入空指针直接段错误。四、链式栈完整实现带头结点1. 头文件定义linkstack#ifndef LINKSTACK_H #define LINKSTACK_H typedef int DataType; typedef struct Node { DataType Data; struct Node *pNext; } Node_t; Node_t *CreateLinkStack(void); int IsEmptyLinkStack(Node_t *pTmpStack); int PushLinkStack(Node_t *pTmpStack, DataType TmpData); DataType PopLinkStack(Node_t *pTmpStack); int DestroyLinkStack(Node_t **ppTmpStack); #endif2. 接口实现linkstack.c#include stdio.h #include linkstack.h #include stdlib.h // 创建链式栈带头结点 Node_t *CreateLinkStack(void) { Node_t *pTmpStack malloc(sizeof(Node_t)); if (NULL pTmpStack) { printf(malloc failed!\n); return NULL; } pTmpStack-pNext NULL; return pTmpStack; } // 判断栈空返回1为空0为非空 int IsEmptyLinkStack(Node_t *pTmpStack) { if (NULL pTmpStack) return -1; return pTmpStack-pNext NULL ? 1 : 0; } // 入栈头插法栈顶为头结点后的第一个节点 int PushLinkStack(Node_t *pTmpStack, DataType TmpData) { if (NULL pTmpStack) return -1; Node_t *pTmpNode malloc(sizeof(Node_t)); if (NULL pTmpNode) { printf(malloc failed!\n); return -1; } pTmpNode-Data TmpData; pTmpNode-pNext pTmpStack-pNext; pTmpStack-pNext pTmpNode; return 0; } // 出栈头删法返回弹出的元素 DataType PopLinkStack(Node_t *pTmpStack) { if (NULL pTmpStack) { printf(栈头指针为空\n); return -1; } if (IsEmptyLinkStack(pTmpStack)) { printf(栈空无法出栈\n); return -1; } Node_t *pTmpNode pTmpStack-pNext; DataType TmpData pTmpNode-Data; pTmpStack-pNext pTmpNode-pNext; free(pTmpNode); return TmpData; } // 销毁链式栈 int DestroyLinkStack(Node_t **ppTmpStack) { if (NULL ppTmpStack || NULL *ppTmpStack) return -1; Node_t *pCur *ppTmpStack; Node_t *pDel NULL; while (pCur ! NULL) { pDel pCur; pCur pCur-pNext; free(pDel); } *ppTmpStack NULL; return 0; }3. 【链式栈易错踩坑点】入栈写成尾插直接覆盖头结点的 next 指针导致旧节点全部丢失、内存泄漏栈中永远只能保存 1 个元素。出栈判空传错指针用栈顶数据节点代替头结点判空逻辑完全错误。节点分配失败未返回malloc 失败后继续执行空指针访问直接触发段错误。链表结构混乱指针赋值顺序错误导致链表断裂、节点丢失。五、队列Queue核心概念1. 核心特性先进先出FIFO, First In First Out/ 后进后出最先存入的元素最先被取出。2. 基础术语队头允许进行出队操作的一端。队尾允许进行入队操作的一端。入队将元素插入到队尾位置的操作。出队将元素从队头位置取出的操作。3. 两种实现方式顺序循环队列底层基于数组实现通过取模运算实现空间循环复用解决假溢出问题。链式队列底层基于单链表实现容量灵活无上限适合数据量不确定的场景。六、链式队列完整实现带头结点1. 接口说明保持与链式栈一致的代码风格接口定义如下Node_t *CreateLinkQueue(void); int IsEmptyLinkQueue(Node_t *pTmpQueue); int EnterLinkQueue(Node_t *pTmpQueue, DataType TmpData); DataType QuitLinkQueue(Node_t *pTmpQueue); int DestroyLinkQueue(Node_t **ppTmpQueue);2. 完整实现代码可直接复用链式栈的linkstack.h结构体定义新建linkqueue.c即可#include stdio.h #include linkstack.h #include stdlib.h // 创建链式队列带头结点 Node_t *CreateLinkQueue(void) { Node_t *pTmpQueue malloc(sizeof(Node_t)); if (NULL pTmpQueue) { printf(malloc queue head failed!\n); return NULL; } pTmpQueue-pNext NULL; return pTmpQueue; } // 判断队空返回1为空0为非空 int IsEmptyLinkQueue(Node_t *pTmpQueue) { if (NULL pTmpQueue) return -1; return pTmpQueue-pNext NULL ? 1 : 0; } // 入队尾插法新节点插入到链表尾部 int EnterLinkQueue(Node_t *pTmpQueue, DataType TmpData) { if (NULL pTmpQueue) return -1; Node_t *pTmpNode malloc(sizeof(Node_t)); if (NULL pTmpNode) { printf(malloc new node failed!\n); return -1; } pTmpNode-Data TmpData; pTmpNode-pNext NULL; // 找到队尾节点 Node_t *pCur pTmpQueue; while (pCur-pNext ! NULL) { pCur pCur-pNext; } pCur-pNext pTmpNode; return 0; } // 出队头删法删除队头节点并返回数据 DataType QuitLinkQueue(Node_t *pTmpQueue) { if (NULL pTmpQueue) { printf(队列指针为空!\n); return -1; } if (IsEmptyLinkQueue(pTmpQueue)) { printf(队空无法出队!\n); return -1; } Node_t *pDel pTmpQueue-pNext; DataType TmpData pDel-Data; pTmpQueue-pNext pDel-pNext; free(pDel); return TmpData; } // 销毁链式队列 int DestroyLinkQueue(Node_t **ppTmpQueue) { if (NULL ppTmpQueue || NULL *ppTmpQueue) return -1; Node_t *pCur *ppTmpQueue; Node_t *pDel NULL; while (pCur ! NULL) { pDel pCur; pCur pCur-pNext; free(pDel); } *ppTmpQueue NULL; return 0; }优化提示当前实现入队需要遍历到尾部时间复杂度 O (n)。工程中通常会额外维护一个队尾指针将入队操作优化为 O (1)初学阶段可先掌握基础逻辑。七、经典排错案例free(): double free detected in tcache 21. 报错含义同一块堆内存被连续调用了两次free()C 标准不允许重复释放glibc 内存管理器检测到后直接终止程序。2. 常见触发场景连续两次调用销毁函数第一次已释放全部内存第二次重复释放。接口内部已经 free 节点外部手动再次 free 该节点。链表结构损坏如入栈写成尾插导致指针混乱销毁循环中重复访问同一块内存。3. 避坑规范每次 free 后立即将对应指针置为 NULL避免野指针。内存释放统一交给销毁函数不要混用手动释放和接口释放。销毁函数入口必须增加空指针判断防御二次调用。确保链表插入删除逻辑正确不出现指针指向混乱、链表断裂。八、学习总结栈和队列本质都是操作受限的线性表核心差异在于操作规则栈后进先出队列先进先出。顺序结构顺序栈、顺序队列优势是访问效率高劣势是容量固定链式结构优势是容量灵活劣势是有指针开销、访问效率略低。C 语言实现数据结构三大高频错误空指针未判断、内存泄漏、重复释放写代码时必须养成防御性编程习惯。链式栈用头插 头删实现 O (1) 的入栈出栈链式队列基础版用尾插 头删实现入队可通过维护尾指针优化效率。

相关新闻

AI大模型开发笔记——开发聊天机器人

AI大模型开发笔记——开发聊天机器人

ollama 大规模预训练语言模型- > 通过ollama技术手段 -> 运行大模型 streamlit

2026/8/17 10:43:33 阅读更多 →
电灭蚊灯哪个牌子好一点?揭秘高人气室内灭蚊灯品牌十大排行榜,夏季必备!

电灭蚊灯哪个牌子好一点?揭秘高人气室内灭蚊灯品牌十大排行榜,夏季必备!

​每年夏天,蚊子总让人抓狂——最近就有新闻报道,某地因连续降雨,蚊虫密度飙升,多地疾控紧急提醒防范登革热。你是不是也在为“灭蚊器哪个牌子好?”这个问题纠结?逛遍电商平台,从几十元到上千元…

2026/8/17 9:32:24 阅读更多 →
Karpathy /raw 笔记法落地排障:知芽如何处理 raw/wiki、引用校验与知识健康

Karpathy /raw 笔记法落地排障:知芽如何处理 raw/wiki、引用校验与知识健康

Karpathy /raw 笔记法落地排障:知芽如何处理 raw/wiki、引用校验与知识健康 现象:本地已经有 raw/、wiki/ 和 CLAUDE.md,但资料摄入、页面维护、引用核对与知识体检仍需要手动处理。环境通常是终端、git、Obsidian,以及由 .md 文…

2026/8/17 14:39:01 阅读更多 →

最新新闻

FPGA 异步 FIFO 乒乓缓存设计:连续采集数据如何完整交给 USB

FPGA 异步 FIFO 乒乓缓存设计:连续采集数据如何完整交给 USB

FPGA 异步 FIFO 乒乓缓存设计:连续采集数据如何完整交给 USB 系列文章:这是「基于 FPGA 和 USB2.0 接口的 CCD 光谱信号采集系统」的数据通路专题之一。上一篇讲 CCD 与 ADC 如何形成采样窗口;本文只聚焦采样数据进入 FPGA 后,怎样通过双异步 FIFO 以“整帧交接”的方式送往…

2026/8/18 10:30:21 阅读更多 →
把40多个平台的直播收进硬盘:开源自动录制工具DouyinLiveRecorder体验记

把40多个平台的直播收进硬盘:开源自动录制工具DouyinLiveRecorder体验记

把40多个平台的直播收进硬盘:开源自动录制工具DouyinLiveRecorder体验记 【免费下载链接】DouyinLiveRecorder 可循环值守和多人录制的直播录制软件,支持抖音、TikTok、Youtube、快手、虎牙、斗鱼、B站、小红书、pandatv、sooplive、flextv、popkontv、t…

2026/8/18 10:30:21 阅读更多 →
抖音批量下载工具推荐:douyin-downloader 免费去水印,3 分钟跑通批量抓取

抖音批量下载工具推荐:douyin-downloader 免费去水印,3 分钟跑通批量抓取

抖音批量下载工具推荐:douyin-downloader 免费去水印,3 分钟跑通批量抓取 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplicatio…

2026/8/18 10:30:21 阅读更多 →
FreeRTOS系统延时原理:从vTaskDelay到任务调度的核心机制

FreeRTOS系统延时原理:从vTaskDelay到任务调度的核心机制

1. 从“延时”说起:为什么FreeRTOS的延时不是简单的“等一等”在嵌入式开发里,尤其是从裸机转向RTOS(实时操作系统)的开发者,第一个需要跨越的认知鸿沟,往往就是“延时”。在裸机里,我们习惯了用…

2026/8/18 10:30:21 阅读更多 →
电脑总在关键时刻睡着?不到几MB的开源防休眠工具NoSleep,让Windows屏幕常亮不再求人

电脑总在关键时刻睡着?不到几MB的开源防休眠工具NoSleep,让Windows屏幕常亮不再求人

电脑总在关键时刻睡着?不到几MB的开源防休眠工具NoSleep,让Windows屏幕常亮不再求人 【免费下载链接】NoSleep Lightweight Windows utility to prevent screen locking 项目地址: https://gitcode.com/gh_mirrors/nos/NoSleep 你的电脑又双叒叕在…

2026/8/18 10:30:21 阅读更多 →
HarmonyOS 7.0 / API 26 FaceAR 预览帧节流:识别和渲染如何避免互相抢帧

HarmonyOS 7.0 / API 26 FaceAR 预览帧节流:识别和渲染如何避免互相抢帧

先看问题为什么会发生 这篇只抓一个点:FaceAR 预览帧节流。我不按概念顺序铺开,而是按项目里最容易出问题的路径来拆:先复现坏写法,再补上边界判断,最后用日志和状态验证结果。 FaceAR 页面最容易把识别、渲染和 UI 动…

2026/8/18 10:29:20 阅读更多 →

日新闻

告别逐帧截图:用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF

告别逐帧截图:用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF

告别逐帧截图:用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 如果你还停留在"看网课 不停暂停 截图 …

2026/8/18 0:00:57 阅读更多 →
思源宋体TTF一站式上手:7个字重免费商用,从下载到上线的完整走查

思源宋体TTF一站式上手:7个字重免费商用,从下载到上线的完整走查

思源宋体TTF一站式上手:7个字重免费商用,从下载到上线的完整走查 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 你是不是也经历过这种时刻:设计稿里…

2026/8/18 0:00:58 阅读更多 →
华硕笔记本控制权回收指南:GHelper 如何用一个 10MB 文件替代 Armoury Crate

华硕笔记本控制权回收指南:GHelper 如何用一个 10MB 文件替代 Armoury Crate

华硕笔记本控制权回收指南:GHelper 如何用一个 10MB 文件替代 Armoury Crate 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, …

2026/8/18 0:00:59 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/18 9:15:35 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/18 9:06:28 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/18 9:04:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/17 18:54:37 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/17 18:55:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/17 18:55:55 阅读更多 →