跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

数据结构:栈的原理与 C 语言实现

栈是一种后进先出(LIFO)的线性表,仅允许在栈顶进行插入和删除操作。基于 C 语言,使用动态数组实现了栈的基本功能,包括初始化、入栈、出栈、获取栈顶元素及销毁等核心接口。重点讲解了动态内存增容策略(2 倍扩容)以及断言检查的重要性,提供了完整的头文件与源文件代码示例,适合用于理解底层数据结构原理及内存管理。

链路追踪发布于 2026/3/27更新于 2026/6/1526 浏览
数据结构:栈的原理与 C 语言实现

数据结构:栈的原理与 C 语言实现

一、栈是什么?

栈(Stack)是一种特殊的线性表,其核心特性在于只允许在固定的一端进行插入和删除操作。这一端被称为栈顶,另一端则称为栈底。栈中的数据元素遵循后进先出(LIFO, Last In First Out)的原则。

注意:这里讨论的是数据结构中的栈,而非操作系统中的调用栈。二者虽然同名,但应用场景和原理有所不同,请勿混淆。

1. 后进先出(LIFO)

所谓后进先出,直观理解就是最后放入的数据最先被取出。这就好比枪上的弹夹:先压进去的子弹后打出去,后压进去的子弹先打出去。

2. 压栈与出栈

  • 压栈(Push):即入栈操作,数据从栈顶插入。
  • 出栈(Pop):即删除操作,数据从栈顶移除。

无论是插入还是删除,操作都集中在栈顶进行。

二、栈的实现思路

1. 存储结构选择

栈可以使用数组或链表实现。相对而言,使用动态数组(顺序表)来实现栈通常更优。虽然顺序表在中间位置插入删除效率较低,但栈的特性决定了我们只在尾部(栈顶)进行操作,避免了中间移动数据的开销,因此数组是更合适的选择。

2. 核心成员定义

参考顺序表的实现,栈的结构体通常包含三个关键成员:

  1. 指向底层数组的指针。
  2. 当前栈内有效元素的个数(size)。
  3. 当前分配的总容量(capacity)。

3. 关键注意事项

实现栈时,必须严格遵守只允许在固定一端操作的原则。不能在两端同时随意操作,否则就失去了栈的意义。因此,我们在代码中应确保所有增删改查逻辑都围绕栈顶索引展开。

三、代码实现细节

本文以 int 类型的栈为例,展示完整的 C 语言实现过程。为了便于类型扩展,我们使用宏定义或 typedef 来抽象数据类型。

1. 头文件声明 (Stack.h)

头文件负责存放函数声明、必要的库函数引入以及类型定义。良好的工程习惯是将接口与实现分离。

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

// 重定义数据类型,方便后续修改
typedef int STDataType;

// 栈结构体定义
 
    STDataType* a;      
     size;           
     capacity;       
} Stack;


 ;
 ;
 ;
 ;
STDataType ;
 ;
 ;
typedef
struct Stack {
// 数组指针
int
// 当前有效元素个数
int
// 当前总容量
// 函数声明
void
StackInit
(Stack* ps)
void
StackDestroy
(Stack* ps)
void
StackPush
(Stack* ps, STDataType data)
void
StackPop
(Stack* ps)
StackTop
(Stack* ps)
int
StackSize
(Stack* ps)
int
StackEmpty
(Stack* ps)

2. 源文件实现 (Stack.c)

源文件包含具体的函数逻辑。在编写过程中,有几个关键点需要特别注意。

初始化与销毁

初始化时需将内存置空,容量设为 0。销毁时不仅要释放内存,还要将指针置为 NULL,防止悬空指针。

#include "Stack.h"

// 初始化栈
void StackInit(Stack* ps) {
    assert(ps); // 断言检查空指针
    ps->a = NULL;
    ps->capacity = 0;
    ps->size = 0;
}

// 销毁栈
void StackDestroy(Stack* ps) {
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->capacity = 0;
    ps->size = 0;
}
入栈与动态增容

当栈空间不足时,需要进行扩容。直接一次性开辟过大空间会浪费内存,过小则会频繁触发扩容降低效率。经验表明,每次扩容为原容量的 2 倍是一个较好的平衡点。

// 入栈
void StackPush(Stack* ps, STDataType data) {
    assert(ps);
    
    // 检查是否需要扩容
    if (ps->size == ps->capacity) {
        int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
        STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity * sizeof(STDataType));
        
        if (tmp == NULL) {
            perror("realloc");
            return;
        }
        
        ps->a = tmp;
        ps->capacity = newcapacity;
    }
    
    ps->a[ps->size] = data;
    ps->size++;
}
出栈与访问

出栈只需减小 size 计数即可,无需真正删除数组末尾的元素,因为下次写入时会覆盖它。获取栈顶元素时,需注意下标是 size - 1。

// 出栈
void StackPop(Stack* ps) {
    assert(ps && ps->size > 0);
    ps->size--;
}

// 获取栈顶元素
STDataType StackTop(Stack* ps) {
    assert(ps && ps->size > 0);
    return ps->a[ps->size - 1];
}

// 获取有效元素个数
int StackSize(Stack* ps) {
    assert(ps);
    return ps->size;
}

// 检测栈是否为空
int StackEmpty(Stack* ps) {
    assert(ps);
    return ps->size == 0;
}

3. 测试用例 (Test.c)

通过简单的测试验证功能是否正常。例如依次压入 1 到 6,然后弹出几个元素并查看栈顶值。

#include "Stack.h"

int main() {
    Stack S;
    StackInit(&S);
    
    StackPush(&S, 1);
    StackPush(&S, 2);
    StackPush(&S, 3);
    StackPush(&S, 4);
    StackPush(&S, 5);
    StackPush(&S, 6);
    
    StackPop(&S);
    int ret1 = StackTop(&S);
    
    StackPop(&S);
    int ret2 = StackTop(&S);
    
    StackPop(&S);
    int ret3 = StackTop(&S);
    
    printf("%d %d %d\n", ret1, ret2, ret3);
    
    StackDestroy(&S);
    return 0;
}

四、总结

栈的实现相对简单,但涉及到的内存管理细节不容忽视。重点掌握以下几点:

  1. LIFO 原则:始终在栈顶操作。
  2. 动态扩容:使用 realloc 配合合理的倍数策略。
  3. 健壮性:利用 assert 处理空指针和非法状态。
  4. 资源清理:程序结束前务必释放动态分配的内存。

理解栈的实现有助于深入掌握内存布局及数据结构的基础逻辑。

目录

  1. 数据结构:栈的原理与 C 语言实现
  2. 一、栈是什么?
  3. 1. 后进先出(LIFO)
  4. 2. 压栈与出栈
  5. 二、栈的实现思路
  6. 1. 存储结构选择
  7. 2. 核心成员定义
  8. 3. 关键注意事项
  9. 三、代码实现细节
  10. 1. 头文件声明 (Stack.h)
  11. 2. 源文件实现 (Stack.c)
  12. 初始化与销毁
  13. 入栈与动态增容
  14. 出栈与访问
  15. 3. 测试用例 (Test.c)
  16. 四、总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 产品经理与 AIGC 产品经理的区别及职业选择指南
  • 2026 AI 编程助手排行榜:文心快码、Copilot 与 Cursor 深度评测
  • AI 产品经理岗位现状:需求与就业前景分析
  • AI 产品经理核心能力体系与学习路径指南
  • PrismLauncher 跨平台安装与配置指南
  • AI 产品经理职业发展路径与核心能力规划
  • 2025 年 9 月 Python 一级编程等级考试真题解析
  • Java Set 接口详解
  • Android 主流三方库源码分析:EventBus
  • AI 产品经理入门指南:核心职责、技能体系与实战路径
  • PentAGI AI 自动化渗透工具 Docker 环境部署指南
  • C++ 继承机制详解:概念、规则与菱形继承
  • 基于 AI 工具的生鲜配送系统快速开发实战
  • AI 产品经理进阶路线图:产业链、分类与核心能力提升
  • Python 纪念币预约自动化工具实现与部署
  • C++11 手写 Promise 实现及与 std::promise 对比
  • B 站 PC 端自动开启字幕用户脚本
  • Prompt 提示词工程核销逻辑与高效 AI 交互策略
  • AI 产品经理核心能力:理解技术原理与用户需求
  • Kimi2.5 核心技术:注意力残差

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online