C语言中的abort()函数会清空栈吗?

8

例如,可以捕获SIGABRT并使用longjump继续程序。

我想知道当我始终调用调用abort()的函数时,这是否会导致堆栈溢出。

我需要知道这一点,因为我想在单元测试中使用assert宏(调用abort)。如果assert失败,我希望继续进行下一个单元测试。


你为什么添加了“stackoverflow”标签? - Govind Balaji
3
@govindo因为他正在谈论一个真正的堆栈溢出问题!你为什么删除了它? - user4815162342
@user4815162342 哦,真的吗?抱歉,他可能应该说“我想知道这是否会导致堆栈溢出”,而不是“我想知道这是否会导致堆栈溢出”。 - Govind Balaji
@govinda 嗯,我想程序员都知道什么是“堆栈溢出”,不需要让那句话变得复杂化... - osiris81
3个回答

2

abort 不需要清空堆栈;longjmp 会将堆栈指针倒回到 setjmp 的位置,从而“清空”堆栈。如果其他一切正确,反复调用 longjmpsetjmp 将不会导致堆栈溢出。

然而,longjmp 会跳过正常的执行路径,这可能会导致资源泄漏。考虑以下代码:

char *s = malloc(...);
... use s ...
free(s);

如果“... use s ...”部分调用了一些函数并从代码中longjmpfree将没有机会被调用,你将会泄漏内存。同样的问题也适用于关闭打开的文件、套接字、释放共享内存段、回收fork出来的子进程等等。
因此,在C编程中很少使用longjmp。我的建议是,如果您不希望程序退出,请避免使用assert。在您的单元测试中使用另一个宏,或者切换到提供其他宏的测试框架。

1

longjmp不会展开堆栈,但它确实修改了堆栈指针:

如果调用setjmp的函数返回,则不再安全使用相应的jmp_buf对象调用longjmp。这是因为当函数返回时,堆栈帧将失效。调用longjmp会恢复堆栈指针,由于函数已经返回,因此堆栈指针将指向不存在且可能被覆盖/损坏的堆栈帧。


3
由于标准C没有析构函数(在C++术语中,所有数据类型都是POD),因此在展开栈和恢复栈指针之间没有区别。 - user4815162342

1
假设采用POSIX,调用abort引发的SIGABRT信号可以通过调用longjmp来跳出,这是有效的,因为abort被指定为异步信号安全的(并且该信号不是异步的)。 (在普通的C语言中,从信号处理程序中执行几乎任何操作都是未定义行为,因此通常不是有趣的讨论案例。)但是,您需要确保没有留下任何导致程序状态不一致的痕迹,以便在程序流程中的其他时间立即或较晚地导致程序调用未定义行为。

话虽如此,我同意用户4815162342的观点,您的提案是一种非常糟糕的错误处理形式。如果您不想中止程序,请勿调用abort,而应编写自己的错误处理函数。


这不是用于错误处理,而仅用于单元测试,并且它按预期工作。就像其他单元测试框架一样,我想在我的测试函数中调用断言宏。一旦一个断言失败,该函数必须被中断并调用下一个测试函数。这正是现在发生的事情。 - osiris81
你可以自己重新定义 assert,但我认为这种方法在单元测试中并没有什么大问题,特别是对于某些类型的单元测试,你甚至可能会使用 SIGSEGV 来做同样的事情... - R.. GitHub STOP HELPING ICE

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