C语言InitStack函数如何正确初始化栈?

99ANYc3cd6
预计阅读时长 19 分钟
位置: 首页 C语言 正文

InitStack 的核心作用是初始化一个栈,在 C 语言中,栈通常使用结构体(struct)动态内存分配(malloc来实现,因为它可以灵活地管理内存大小,并且是面向对象思想的一种简单体现。

c语言 InitStack
(图片来源网络,侵删)

下面我将分步讲解,从最基础的概念到完整的代码实现。


栈的基本概念

栈是一种后进先出 的数据结构,它只允许在一端进行插入和删除操作,这个端部被称为栈顶

  • 压栈:向栈顶添加一个元素。
  • 出栈:从栈顶移除一个元素。
  • 栈顶指针:始终指向栈顶元素。

栈的数据结构设计

在 C 语言中,我们通常用一个结构体来表示一个栈,这个结构体至少需要包含两个部分:

  1. 存储数据的数组或指针:用于存放栈中的元素。

    c语言 InitStack
    (图片来源网络,侵删)
    • 静态数组:大小在编译时就确定了,不够灵活。
    • 动态数组:使用指针指向一块动态分配的内存,可以在运行时调整大小,更常用。
  2. 栈顶指针:一个整数,用来记录当前栈顶元素在数组中的位置。

下面是一个典型的栈结构体定义:

#define MAXSIZE 100 // 假设栈的最大容量为100 (静态方式)
// 或者
// typedef int ElemType; // 定义栈中元素的类型,可以是 int, char, struct 等
// 栈的结构体定义
typedef struct {
    int *data;         // 动态分配的数组,用于存储栈元素
    int top;           // 栈顶指针,初始为 -1
    int capacity;      // 栈的当前最大容量
} Stack;

为什么用动态内存? 使用 int *datacapacity 可以让栈的大小在需要时进行扩容(当 top == capacity - 1 时,分配一个更大的空间),这使得我们的栈实现更加健壮和通用。


InitStack 函数的实现

InitStack 函数的任务就是创建一个空栈,对于一个空栈,它的状态应该是:

c语言 InitStack
(图片来源网络,侵删)
  • data 指向一块有效的内存空间。
  • top 的值为 -1(表示栈中没有元素)。
  • capacity 有一个初始值。

下面是 InitStack 函数的完整代码。

完整代码示例

#include <stdio.h>
#include <stdlib.h> // 需要包含这个头文件来使用 malloc 和 free
// 1. 定义栈中元素的类型(这里用 int 作为示例)
typedef int ElemType;
// 2. 定义栈的结构体
typedef struct {
    ElemType *data;    // 指向动态分配的数组
    int top;           // 栈顶指针
    int capacity;      // 栈的容量
} Stack;
// 3. 初始化栈函数
// 参数:s - 一个指向 Stack 结构体的指针(我们想初始化这个栈)
// 返回值:无返回值 (void)
// 功能:为栈分配初始内存,并将栈顶指针和容量设置为初始值
void InitStack(Stack *s) {
    // 1. 为 data 指针分配一块初始内存
    // 这里我们先给一个初始容量,10
    s->capacity = 10;
    s->data = (ElemType *)malloc(s->capacity * sizeof(ElemType));
    // 2. 检查内存是否分配成功
    if (s->data == NULL) {
        fprintf(stderr, "内存分配失败!\n");
        exit(1); // 分配失败则退出程序
    }
    // 3. 将栈顶指针初始化为 -1,表示这是一个空栈
    s->top = -1;
}
// --- 其他辅助函数 (为了完整性) ---
// 判断栈是否为空
int IsEmpty(Stack *s) {
    return s->top == -1;
}
// 判断栈是否已满
int IsFull(Stack *s) {
    return s->top == s->capacity - 1;
}
// 压栈 (Push)
void Push(Stack *s, ElemType value) {
    if (IsFull(s)) {
        printf("栈已满,无法压栈!\n");
        // 这里可以添加扩容逻辑,
        // s->capacity *= 2;
        // s->data = (ElemType *)realloc(s->data, s->capacity * sizeof(ElemType));
        return;
    }
    s->top++;           // 栈顶指针上移
    s->data[s->top] = value; // 将新元素存入栈顶
}
// 出栈 (Pop)
void Pop(Stack *s) {
    if (IsEmpty(s)) {
        printf("栈为空,无法出栈!\n");
        return;
    }
    s->top--; // 栈顶指针下移,元素被“逻辑上”移除
}
// 获取栈顶元素 (Peek/GetTop)
ElemType GetTop(Stack *s) {
    if (IsEmpty(s)) {
        printf("栈为空,没有栈顶元素!\n");
        return -1; // 假设元素不可能是 -1
    }
    return s->data[s->top];
}
// 销毁栈,释放内存
void DestroyStack(Stack *s) {
    free(s->data); // 释放 data 指向的内存
    s->data = NULL; // 避免成为野指针
    s->top = -1;
    s->capacity = 0;
}
// 4. 主函数:测试我们的栈和 InitStack 函数
int main() {
    Stack myStack; // 声明一个栈变量
    // 调用 InitStack 函数来初始化 myStack
    InitStack(&myStack);
    printf("栈初始化完成!\n");
    printf("初始容量: %d\n", myStack.capacity);
    printf("初始栈顶指针: %d\n", myStack.top);
    // 测试压栈
    printf("\n--- 压栈测试 ---\n");
    Push(&myStack, 10);
    Push(&myStack, 20);
    Push(&myStack, 30);
    printf("压栈 10, 20, 30 后,栈顶元素是: %d\n", GetTop(&myStack));
    // 测试出栈
    printf("\n--- 出栈测试 ---\n");
    Pop(&myStack);
    printf("出栈一次后,新的栈顶元素是: %d\n", GetTop(&myStack));
    // 测试栈是否为空
    printf("\n--- 栈状态测试 ---\n");
    if (IsEmpty(&myStack)) {
        printf("栈为空,\n");
    } else {
        printf("栈不为空,\n");
    }
    // 销毁栈
    printf("\n--- 销毁栈 ---\n");
    DestroyStack(&myStack);
    // 此时再访问 myStack.data 是不安全的,因为它已经被释放了
    printf("栈已销毁,\n");
    return 0;
}

代码详解

  1. typedef struct { ... } Stack;

    • 这行代码定义了一个名为 Stack 的结构体类型,之后我们就可以像使用 int, char 一样使用 Stack 来声明变量了。
  2. *`void InitStack(Stack s)`**

    • *参数 `Stack s**:我们传递的是一个**指针**,而不是Stack s`,为什么?
      • 效率:结构体可能很大,直接传递整个结构体会产生一份巨大的副本,效率很低,传递指针(通常是一个机器字的大小)效率高得多。
      • 修改原对象:C 语言是“值传递”,如果我们传 Stack s,函数内部对 s 的任何修改(s->top = -1)都只是修改了那份副本,不会影响到 main 函数中的 myStack,只有通过指针,我们才能直接操作 main 函数中的 myStack 变量本身,这是 C 语言中修改外部变量的标准做法。
  3. s->data = (ElemType *)malloc(s->capacity * sizeof(ElemType));

    • malloc 是 C 标准库函数,用于在上分配一块内存。
    • s->capacity * sizeof(ElemType) 计算需要的总字节数。capacity 是 10,ElemTypeint(4 字节),那么就需要 10 * 4 = 40 字节。
    • (ElemType *) 是将 malloc 返回的 void* 类型的指针强制转换为我们的 ElemType* 类型。
    • 这行代码执行后,s->data 就指向了一块可以存放 s->capacityElemType 类型数据的内存空间。
  4. s->top = -1;

    • 这是初始化一个空栈的关键。top-1 表示数组中没有任何元素是有效的,当第一个元素压入时,top 会先变为 0,然后将元素存入 data[0]
  5. if (s->data == NULL)

    • 这是一个非常重要的健壮性检查。malloc 可能会因为内存不足而失败,此时它会返回 NULL,如果不检查,直接使用 NULL 指针(s->data[0] = ...)会导致程序崩溃(段错误)。

InitStack 是使用 C 语言实现一个栈数据结构时的第一步,也是至关重要的一步,它的主要职责是:

  1. 分配内存:为存储栈元素的数组在堆上申请空间。
  2. 设置初始状态:将栈顶指针 top 设置为 -1,表示栈为空。
  3. 设置初始容量:为栈设定一个初始的容量,为后续可能的扩容做准备。
  4. 错误处理:检查内存分配是否成功,确保程序的稳定性。
-- 展开阅读全文 --
头像
C语言如何实现URL编码?
« 上一篇 03-02
dede网站如何被挂马?安全漏洞在哪?
下一篇 » 03-02

相关文章

取消
微信二维码
支付宝二维码

目录[+]