由于realloc,程序崩溃了。

3

我正在尝试构建一个可以自动调整大小的堆栈,但是使用realloc()时程序崩溃了。

构造器:

Stack::Stack()
{
   st = (int*)malloc(sizeof(int));
   sp = 0;
   length = 1;
}

这是我的 add() 函数:
void Stack::add(int item)
{
   if (sp == length)
       Stack::resizeStack(&st, &length, 1);
   st[sp++] = item;
}

调整大小功能(我使用变量a以便能够重复使用它进行弹出):

void Stack::resizeStack(int **st, int *length, bool a)
{
   if (a == 1)
       *length *= 2;
   else
       *length /= 2;
   realloc(*st, sizeof(int) * (*length));
}

这是我测试栈的方法:

Stack* myStack = new Stack();
for (int i = 0; i < 10; i += 1) {
    myStack->add(i);
    cout << myStack->getStackSize() << '\n';
}
free(myStack);

我注意到程序在 for 结束时崩溃了。

如果有人能解释一下我做错了什么,我会很感激。


5
我需要问一下,为什么你在一个C++程序中使用mallocfreerealloc?(原文已翻译) - PaulMcKenzie
2
两个问题:您没有维护长度,而且您错误地使用了realloc。 - user2249683
2
你正在使用free释放通过new分配的内存,但是你一开始就不需要使用new - emlai
4
此外,您混淆了使用newfreeStack *myStack = new Stack(); ... free(myStack); 这是未定义的行为。看起来您正在从C书籍和/或教程中学习C ++。 - PaulMcKenzie
2
我只是为了对象使用了'new'和'free'的组合,甚至没有意识到这一点。尽管如此,即使我注释掉该行或使用'delete',它仍会崩溃。@Dieter Lücking 我在'length'中维护长度,请告诉我使用realloc的正确方法是什么? - Razvan Meriniuc
@RazvanMeriniuc 既然重新分配例程在你的类内部,而且你的堆栈类实际上跟踪其当前大小,为什么不直接操作 this->lengththis->st。然后您可以删除一些星号。此外,为什么要缩小尺寸。通常,动态数组只会增长,但它可能有一个 shrink_to_fit 方法,可以手动调用。 - user877329
4个回答

4

你很幸运遇到了崩溃。这是未定义的行为。

让我们来看看Bjarne Stroustrup在这里所说的:

不,因为你不能使用malloc()分配对象并使用delete释放它。也不能使用new分配并使用free()删除,或者在由new分配的数组上使用realloc()。

C ++操作符new和delete保证正确的构造和销毁;需要调用构造函数或析构函数时,它们会被调用。C样式的函数malloc(),calloc(),free()和realloc()没有保证。此外,new和delete用于获取和释放原始内存的机制无法保证与malloc()和free()兼容。如果在您的系统上混合样式有效,则您只是“幸运” - 现在而已。

C++ FAQ 也有专门的条目:

https://isocpp.org/wiki/faq/freestore-mgmt#realloc-and-renew


4
所有那些说在C++中使用 malloc()free() 是一个坏主意的人是100%正确的。建议使用 newdelete 替代 malloc()free(),并且推荐使用标准库容器而不是自己编写的堆栈实现。
无论如何,这里真正的问题是,realloc() 可能会分配一个新的内存块并释放旧的内存块。它返回指向新内存块的指针。
正确的调用方式是:
*st = realloc(*st, sizeof(int) * (*length));

现在 *st 将存储新的指针,一切都没问题了。
考虑使用标准库,而不是实现自己的基本数据结构。它具有良好设计的接口,并且经过非常彻底的测试。

1
我正在实现这个项目,以便学习C++和数据结构。我没有意识到我在对象上使用了free。尽管如此,realloc是导致问题的原因。它按照你说的方式工作了。我误解了它的工作原理。我使用malloc()和realloc()的原因是我没有找到用new重新分配内存的方法。感谢您的耐心! - Razvan Meriniuc

2
在程序末尾崩溃的原因很可能是您混合使用了newfree。这是未定义的行为。它可能在某些系统上运行良好,但从来不是一个好主意。您应该将newdelete配对使用。调用mallocfree配对使用,但这更适用于C代码。C++代码通常使用newdelete
当然,您可以通过使myStack成为局部变量来消除new
Stack myStack;

您还需要调整成员访问方式,使用 . 代替 -> 。不需要删除myStack,因为一旦函数退出(包括异常情况),本地变量将自动清除。
此外,请注意 realloc 的返回值被忽略。如果当前内存块无法通过 realloc 扩展,则必须分配一个新的内存块并将旧数据复制到其中。在这种情况下,将返回一个新指针,因此必须更新 st
*st = realloc(*st, sizeof(int) * (*length));

但是,在C++代码中使用mallocreallocfree有点奇怪。

如果你被迫使用手动内存管理数组(比如学习),可以使用new[]delete[],或者使用vectorstack类来进行更严肃的编码。


C++代码会为看到newdelete而感到尴尬。使用std::vector或者放弃。 - Puppy
1
@Puppy:任何主流的std::vector实现中是否使用了realloc - Lightness Races in Orbit
1
@Daniel Stevens 感谢您的反馈。问题出在我的realloc上。我不知道如何使用new或delete重新分配内存,所以我使用了这些函数。 - Razvan Meriniuc
我不知道,这是一个没有意义的实现细节。 - Puppy
我认为C++故意省略了类似简单realloc的功能,因为它可能在某些数据类型上不安全。考虑一个具有指向自身的内部指针的对象。如果通过realloc进行位复制移动,则其内部指针现在将指向其先前的内存,这将是不正确的。相反,可以通过将对象复制构造到新的内存位置并在旧位置上解构来移动对象。 - Daniel Stevens
@DanielStevens 谢谢你的解释!self指针很有道理。 - Razvan Meriniuc

-2
使用 std::vector,它会自动处理所有内存管理。事实上,在 std::stack 存在的情况下,你几乎不需要 Stack 类,但那是另一回事。
此外,为测试不要以超级无意义的方式动态分配堆栈,只需创建一个本地堆栈即可。

2
这最多只是一条注释。 - Lightness Races in Orbit
1
我猜这是为了一个作业或者学习练习,让学生构建一个基于数组的栈类。 - crashmstr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接