【问题标题】:Allocating memory to my stack dynamically (only if needed)动态分配内存到我的堆栈(仅在需要时)
【发布时间】:2025-11-22 02:55:01
【问题描述】:

我需要为我的结构内的一个数组分配内存,这个数组在我定义结构时没有定义的大小:

typedef struct stacks {
    int size; // Stores the size of my -values- array
    int sp; //points to the top of the stack, my stackpointer
    int *values;
} STACKS;

所以,为了初始化我的结构,我编写了这个函数,它为我的数组分配(使用 calloc?)内存,然后我将数组的新大小放入 SIZE 变量中。

#define MAXIMUM 10

int initStacks(STACKS *s){
    s->values = calloc(MAXIMUM,sizeof(int));
    s->size = MAXIMUM;
    s->sp = 0;
    return 0;
}

现在,如果我想将某些东西推到堆栈顶部(LIFO),我会使用这个:

int pushs(STACKS *s, int x){
    if (s->sp==s->size) {
        realloc(s->values, MAXIMUM * sizeof(int));
        s->size*=2;
    }

    s->values[s->sp]=x;
    s->sp++;
}

这是正确的做法吗?

realloc 在我的函数中是否正常工作?

非常感谢您的帮助!

编辑:

这会更有意义吗?这样,我真的不需要声明数组的值,即用#define maximum 10 定义的值

typedef struct stacks {
    int size; // guarda o tamanho do array valores
    int sp;
    int *values;
} STACKS;


int initStacks(STACKS *s){
    s->values = calloc(1,sizeof(int));
    s->size = 1;
    s->sp = 0;
    return 0;
}

int isEmptys(STACKS *s){
    return((s->sp)==0);
}

int pushs(STACKS *s, int x){
    s->size++;
    realloc(s->values, s->size * sizeof(int));
    s->values[s->sp]=x;
    s->sp++;
}

【问题讨论】:

  • 您至少应该检查 realloc 的返回值,如果为 NULL 则请求失败,您必须重新(al)定位堆栈。 s->size==MAXIMUM 的比较也有点可疑,也许应该是 s->sp==MAXIMUM
  • 伙计,这种比较当然是愚蠢的!谢谢你指出!也许更好的是s->sp==s->size?这意味着堆栈指针到达了等于最大值的点?
  • 你两次展示了initStacks函数。
  • @KeithThompson 你是对的,对不起。试图仔细编写问题并最终添加了两次相同的功能。
  • 正如@FoggyDay 所写,我不会为每个元素重新分配,但例如每次加倍大小。因此,您丢失的内存不超过 50%。或者,您可以使用完全不同的结构:每个节点都包含一些元素的链表。这完全避免了 realloc,包括它的陷阱(碎片,内存不足)。以防万一您计划做更大的事情;-)

标签: c


【解决方案1】:

假设您有一个原始尺寸因子(名称 capacity 将是适当的,如果不是更多的话),您的原始代码缺少几件事:

  • 将大小与常数进行比较,而不是将当前 sp 与堆栈当前大小进行比较。
  • 保存也不测试realloc的返回结果
  • 实际上并没有将分配加倍(您在 realloc 表达式中缺少 2x。
  • 声明int返回结果,但不存在这样的返回。
  • 无法将推送结果(成功与否)反馈给调用者。顺便说一句,缺少的返回结果将是理想的选择。

解决所有这些问题:

int pushs(STACKS *s, int x)
{
    if (s->sp == s->size) 
    {
        void *pv = realloc(s->values, 2 * s->size * sizeof *(s->values));
        if (pv != NULL)
        {
            s->values = pv;
            s->size *= 2;
        }
        else
        {
            fprintf(stderr, "Failed to resize stack\n");
            return -1;
        }
    }

    s->values[s->sp++] = x;
    return 0;
}

未经测试,但希望足够接近。

祝你好运

【讨论】:

  • 非常感谢@WhozCraig,事实上,Telo 警告过我关于无意义的比较。我将使用您的见解转换我的代码,并返回一个结论:)
  • 一件事,没有 MAXIMUM 并通过向数组添加一个 (int) 单元来继续重新分配是否有意义?
  • @Olaf 谢谢!一个问题,我现在如何 free() ?因为我不想在删除数组的一个元素时释放所有分配的内存!
  • @skills:堆栈是一种数据结构,它只定义推送和弹出操作(我想你已经意识到了这一点)。如果您考虑缩小堆栈,那么您选择的结构的一般答案是不这样做。一旦您不再需要完整的堆栈,只需按原样保留内存和free()。如果这是一个问题,您很可能会更喜欢我在另一条评论中提到的链表这样的不同方法。但是,当从堆栈中弹出时,您可能会重新分配相反的内容:sp < size/2 左右。我会保持最小尺寸,但出于效率原因,
  • Craig,如果return -1return 0 背后的想法是返回一个布尔结果,那么最好使用正逻辑:成功时返回true。此外,返回 1,而不是 -1,因为这是布尔“true”的正确整数值。更好的是:从一开始就声明函数bool,并完全避免使用整数。
【解决方案2】:

虽然不是对实际问题的直接回答,而是对一般问题的更多回答,但我将其发布,因为它不适合评论。

如果您预计会有过多的 push/pop 操作和内存使用,以下可能是替代方案:

typedef struct SubStack_s {
    struct SubStack_s *prev;
    int data[ENTRIES_PER_SEGMENT];
} SubStack;

typedef struct {
    SubStack *tos;    // init to NULL
    size_t sp;        // init to 0
} Stack;

基本思想是将元素推入每个子堆栈直到填满(就像您已经做的那样)。如果当前已满,则分配一个新的,链接它们(new->prev = old)并继续使用新的(将new 存储到Stack.tos

Pop 的工作方式类似,一旦不再使用,就会释放每个子堆栈。

这个概念被称为“碎片堆栈”。它比 realloc 方法更有效(它避免了复制),并且不会将 RAM 碎片化,因为所有块的大小都相同。哦,它允许有指向堆栈的指针,而 realloc-variint 不允许,因为堆栈的地址可以改变。

【讨论】:

  • 您知道操作系统在调用realloc 时为了避免复制而应用的技术吗?
  • @undefinedbehaviour:不,我几十年前才开始从事这项工作,但仍然不了解基础知识(注意:讽刺-只是为了确保!)。您是否知道与简单的列表附加(尤其是单线程代码)相比,操作系统调用和页表操作实际上是多么昂贵(时钟)?
  • 好吧,我会咬...你打算如何通过使用链表来避免分配(以及操作系统调用)?在过去的几十年中发生了很多变化,如果您没有跟上时代,那么根据年龄断言某种优势是错误的,所以请...只是将古代历史排除在讨论之外。
  • @undefinedbehaviour:我可以补充说我生活在一个没有互联网的岛上,只是坚持当时的旧做法。请注意,对于 malloc(),这与 realloc() 一样首先是一个库调用。但是 malloc() 可以(并且确实)管理内部空闲列表,而 ralloc 这样做会有很多问题(当然,两者都使用相同的 fre-list 和堆),因为它必须预测哪个块将需要延长。 Argumentum ad antiquitatem 当然不是我所做的。请使用您的讽刺翻译。 ...
  • realloc 的出现不是表明标准库和操作系统开发人员可能会对堆数据结构进行编程以期望分配增长吗? realloc目前在管理这个内部空闲列表时遇到了哪些问题?您知道当今的操作系统通常如何管理内存碎片吗?