【问题标题】:Free the stack implemented in C释放用 C 实现的堆栈
【发布时间】:2019-02-13 14:36:00
【问题描述】:

我在 C 中实现了一个堆栈及其函数。现在在调用了所有函数之后,我想释放堆栈。

我的问题是我应该先释放堆栈“st”的基指针还是直接释放堆栈“st”?两者似乎都适用于我的代码。

#include <stdio.h>
#include <stdlib.h>

#define init_size 10
#define increment 1

typedef struct sqStack
{
    int* top;
    int* base;
    int stack_size;
}sqStack;

int init_stack(sqStack* sq)
{
    if(sq->base==NULL)
    {
       sq->base = (int*)malloc(init_size*sizeof(int));
    }
    if(sq->base==NULL) exit(-1);
    sq->stack_size=init_size;
    sq->top=sq->base;
    return 1;
}
int push(sqStack* sq, int e)
{
    if(sq==NULL) exit(-1);

    if(sq->top-sq->base==sq->stack_size-1)//pointer top reaches the top of the stack
    {
        int* q = (int*)realloc(sq->base,(sq->stack_size+increment)*sizeof(int));
        if(q==NULL)  exit(-1);
            sq->base=q;
        sq->top=sq->base+sq->stack_size-1;
            sq->stack_size += increment;

    }
    *sq->top++=e;
    return 1;
}
int pop(sqStack* sq,int* e)
{
    if(sq==NULL) exit(-1);
    if(sq->base==sq->top)  exit(-1);
    sq->top--;
    *e=*sq->top;
    sq->stack_size--;
    return *e;
}
int top(sqStack* sq,int* e)
{
    if(sq==NULL) exit(-1);
    if(sq->base==sq->top)  exit(-1);
    *e=*(sq->top-1);
    return *e;
}
int empty(sqStack* sq)
{
    if(sq->base==sq->top) return 1;
    else return 0;
}

int main() {
    sqStack* st= (sqStack*)calloc(1,sizeof(sqStack))  ;
    int e;
    init_stack(st);
    for(int i=0;i<12;i++)
    {
        push(st,i+1);

    }
    for(int i=0;i<12;i++)
    {
        printf("%d\n",top(st,&e));
        printf("%d\n",pop(st,&e));
    }
    free(st->base);
    free(st);
    return 0;
}

结果是: 12 12 11 11 10 10 9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 每个数字位于一行。

【问题讨论】:

    标签: c stack free


    【解决方案1】:

    mallocfree 而言,您所拥有的是正确的。

    您应该只将mallocrealloc 返回的内容传递给free。您为结构和堆栈分配空间并释放两者,因此您不会泄漏任何内存。

    您不想先free(st),因为这样做之后st 指向的内存不再有效,并且您不能随后安全地free(st-&gt;base)。此外,因为free 对给定内存的内容一无所知,所以它不会尝试释放内存可能包含的任何指针。所以只是调用free(st) 会泄漏st-&gt;base 中的内存。

    【讨论】:

    • 所以我不需要释放的原因(st->top);是因为 st->top=st->base;在我弹出堆栈中的所有元素之后??
    • @Liu 如果堆栈是空的,那么它们是相同的,所以你不想释放两次。但即使堆栈不为空,您也不想这样做。在这种情况下,st-&gt;top 将指向数组中间的某个位置,这不是mallocrealloc 返回的地址。
    • 如果释放栈st,是只释放st的首地址还是整个数组??
    • @Liu 如果你告诉malloc 分配例如 100 个字节并返回地址 0x1234,当你将 0x1234 传递给 free 时,它会释放它给你的 100 个字节。
    • 所以在我的情况下,如果指针 top 位于数组的中间,并且在我释放 base 之后,top 也会被释放?
    【解决方案2】:

    每个分配都应与相应的free 配对,因此在您的情况下,这意味着释放st-&gt;basest(按此顺序)。但是您的程序仅在终止之前立即执行其释放,并且当程序终止时,操作系统将回收它未释放的任何内容。 Valgrind 之类的内存使用分析器可以为您检测到差异,但实际上这并不重要。

    【讨论】:

      【解决方案3】:

      您的代码是正确的。相反的顺序可能会起作用,但如果确实如此,这将是偶然的。

      原因是free(st) 释放了存储对象*st 的内存。指针st-&gt;base 存储在该内存的一部分中。假设某个其他任务或线程一被释放就获得了相同的内存。然后怎样呢?也就是说,当free(st-&gt;base) 最终被调用时会发生什么?

      答案:未定义会发生什么。

      即使仍然可以从st-&gt;base 检索地址(并且可能无法),该地址也可能已被任意数据覆盖,在这种情况下free(st-&gt;base) — 将任意数据解释为地址——会要求操作系统解除分配...好吧,你不知道什么它会要求操作系统解除分配。在这种情况下,人们几乎不应该期望有好的结果。

      你做得很好。你的排序是正确的。

      其他注意事项

      为了安全起见,现代操作系统内核有时会自动用空数据或随机数据覆盖已释放的内存。此外,它们有时会撤销程序对硬件页面的访问,通过该硬件已(实际上)处理已释放的内存,或者收紧允许访问的范围。其中一些比其他更有可能,但我已经看到这三个中至少有两个发生。关键是,根据内核最新的内存管理和安全算法,内核可以根据内核的喜好立即、迟早或根本不做这些事情,因为你的程序不应该关心释放后会发生什么程序释放后的内存。

      【讨论】:

      • 所以我不需要释放的原因(st->top);是因为 st->top=st->base;在我弹出堆栈中的所有元素之后??
      • 我没有仔细检查你所有的代码,但只检查了这两行。不幸的是,虽然我昨天有一些时间,但下周我将没有更多的时间,但其他人可能会建议你。祝你好运。
      猜你喜欢
      • 1970-01-01
      • 2018-06-01
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-29
      • 1970-01-01
      • 2010-11-26
      相关资源
      最近更新 更多