当您在 main 中动态分配内存时,如何处理函数中的 assert()?

Posted

技术标签:

【中文标题】当您在 main 中动态分配内存时,如何处理函数中的 assert()?【英文标题】:How to deal with assert() in a function, when you have dynamically allocated memory in main? 【发布时间】:2018-03-01 13:36:22 【问题描述】:

我有以下 C 函数:

void mySwap(void * p1, void * p2, int elementSize)

    void * temp = (void*) malloc(elementSize);
    assert(temp != NULL);
    memcpy(temp, p1, elementSize);
    memcpy(p1, p2, elementSize);
    memcpy(p2, temp, elementSize);
    free(temp);

我想在通用排序函数中使用。假设我使用它对 main() 拥有的动态分配的数组进行排序。现在让我们假设在 mySwap() 中的某个时间点 temp 实际上为 NULL,并且整个程序在没有释放 main() 中动态分配的数组的情况下中止。我认为 mySwap() 和排序函数都可以返回一个 bool 值,指示分配是否成功,并且通过使用 if 语句我可以在 main() 和 exit(EXIT_FAILURE) 中释放数组,但似乎没有就像一个非常优雅的解决方案。在这种情况下,防止内存泄漏的好方法是什么?

【问题讨论】:

如果你的程序被中止了,你怎么会有内存泄漏? 在 malloc 之后,temp 永远不会改变(从未分配给它),*temp 可以改变,但从未经过测试。 @joop,请参阅memcpy。该代码交换了两个不同大小的元素。 附带说明,也许更好的方法是在对 mySwap 的调用之间共享状态/上下文。如果您的排序不是多线程的,则不必在每次调用时分配(即mySwap(void * state, void * a, void * b, int elementSize)),或者让您的调用函数准备一个临时缓冲区并将其传递给mySwap 我看到人们不必要地将malloc() 转换为某种指针类型,但malloc() 返回void *,因此您的转换不仅不需要,而且也是多余的。此外,交换 指针 不需要复制。 【参考方案1】:

assert 通常在调试期间用于识别不应该发生的问题/错误。

内存不足是可能发生的,因此assert 不应该处理,或者,如果您确实使用assert,请注意它会中止程序。一旦程序中止,程序使用的所有内存都会被释放,所以不用担心。

注意:如果您不想到处使用笨重的if 语句来处理几乎不会发生的错误,您可以使用setjmp/longjmp 来返回可恢复状态。

【讨论】:

【参考方案2】:

您必须意识到 malloc 失败的原因是您的计算机内存不足。从那时起,您的程序就没有任何意义可做,除了尽可能优雅地终止。

操作系统将在程序终止时释放内存,因此您无需担心。

不过,在正常情况下,手动free() 自己当然是个好习惯。与其说是为了“使内存再次可用” - 操作系统将确保这一点 - 而是为了验证您的程序在此过程中没有出现严重错误并造成堆损坏、泄漏或其他错误。如果你的程序中有这样的错误,它会在free() 调用期间崩溃,这是一件好事,因为错误会浮出水面。

assert 最好不要在生产代码中使用。如果需要,构建自己的错误处理,这比在执行过程中暴力终止自己的程序要好。

【讨论】:

特别是不要在 MS Windows 上使用 assert(),这会导致一些愚蠢的操作系统技工开始在互联网上搜索解决方案,为什么你,一些未知程序的程序员,称为 assert .为什么微软认为他们可以在互联网上找到原因,我不知道。到目前为止,Windows从未找到一个“解决这个问题的方法”,但这不会阻止它尝试,并在你的脸上发送 100% 无用的消息框。【参考方案3】:

不要使用malloc来避免这个问题。

不是为每个交换分配一块内存,而是一次交换一个字节;

for (int i = 0; i < elementSize; ++i) 
  char tmp = ((char*)p1)[i];
  ((char*)p1)[i] = ((char*)p2)[i];
  ((char*)p2)[i] = tmp;

【讨论】:

是的,如果可能的话,就像给定的确切情况一样,将算法更改为使用固定数量的自动内存而不需要更长时间是最好的。但可能并非在所有情况下都有效。 @deduplicator:在什么情况下会失败? (请记住,问题标记为c,C 标准允许逐字节复制。) 好吧,也许实际上他不需要进行交换,但需要进行一些其他可笑的复杂计算。仍然得到 +1,因为如果它是交换或类似的东西,你有最好的想法,而其他所有东西都是过度设计和容易出错的。 啊,我想你的意思是一个假设的库函数,它需要临时存储以用于交换值以外的目的。不过,这似乎与实际提出的问题有点距离。 @deduplicator。当然,这是可能的。 Stdio 必须为每个缓冲的 FILE(以及 FILE 结构本身)分配一个缓冲区,这肯定会失败。但是和qsort不同的是,fopen的API是有办法报错的。【参考方案4】:

仅在开发过程中使用assert() 来捕获程序员错误,在发布版本中它不会做任何事情。如果您需要测试其他内容,请使用适当的错误处理,无论这意味着 abort()、返回代码还是使用 setjmp()/longjmp() 模拟异常。

顺便说一句,do not cast the result of malloc()

【讨论】:

@Lundin:毫无疑问,至少非常麻烦。是的,出于这个原因,这通常是一个非常糟糕的主意,但可以非常优雅。

以上是关于当您在 main 中动态分配内存时,如何处理函数中的 assert()?的主要内容,如果未能解决你的问题,请参考以下文章

具体来说,fork() 如何处理 Linux 中 malloc() 动态分配的内存?

JavaScript中的内存泄漏以及如何处理

由于内存导致重新分配失败时如何处理?

如何处理构造函数的失败?

如何处理 Spring 安全中的动态角色变化?

类内的动态内存分配