当您在 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()?的主要内容,如果未能解决你的问题,请参考以下文章