假设我在c文件中有以下主要内容
int f();
int main(){
//terminate f() if in infinite loop
return f();
}
然后是一个单独的 C 文件,该文件可能包含以下内容:
int f() {
for(;;) {}
return 0;
}
有没有办法检测函数 f() 处于无限循环中并从主函数内部终止它的执行?
编辑: 我需要这个功能,因为我正在编写一个测试平台,其中调用的函数可能有一个无限循环 - 这就是我最终要检查的内容。因此,无论如何我都无法修改 f()。我也在 Linux 环境中。
不,没有办法明确确定函数是否包含无限循环。
但是,我们可以做一些假设来检测潜在的无限循环并在程序中优雅地退出程序(例如,我们不必按Ctrl+C)。 此方法在 JS 中使用的几个测试框架中很常见。 基本上,我们为函数完成设置了一些任意的时间限制。 如果函数未在该时间限制内完成,我们假设它不会完成并抛出错误。
在 C/C++ 中,如果你在 Unix 系统上,你可以用 pthreads 实现这一点。 在Windows中,您将使用windows.h
。 我只有pthreads
的经验,所以我将展示一个简单的例子,说明如何使用 pthreads 来做到这一点。
#include <pthread.h> // Load pthread
#include <signal.h> // If f() does not exit, we will need this library to send it a signal to kill itself.
#include <stdbool.h> // You could use an int or char.
#include <stddef.h> // Defines NULL
#include <unistd.h> // Defines sleep()
bool testComplete; // Has the test completed?
/**
* The function being tested.
*/
void f() {
while(true);
}
/**
* This method handles executing the test. This is the function pthread will
* use as its start routine. It takes no arguments and returns no results.
* The signature is required for pthread_create().
*/
void *runTest(void *ptr) {
testComplete = false;
f();
testComplete = true;
}
int main() {
pthread_t testThread;
pthread_create(&testThread, NULL, runTest, NULL); // Create and start the new thread. It will begin executing runTest() eventually.
sleep(5); // Give it 5 seconds to complete (this should be adjusted or could even be made dynamic).
if(testComplete) {
// Test completed successfully.
pthread_join(testThread, NULL);
} else {
// The test did not exit successfully within the time limit. Kill it and you'll probably what to provide some feedback here.
pthread_kill(testThread, SIGPIPE); // There are other signals, but this one cannot be ignored or caught.
}
}
要编译它,您需要执行gcc your_filename.c -o output_filename -lpthread
。
如果您希望该程序同时在Unix和Windows系统上运行,则可能需要考虑制作一些统一的接口来访问线程,然后将特定于操作系统的接口调整到您的接口中。 这将使事情变得更简单,尤其是在扩展此库时。
您可以在不同的线程中调用f()
,并在达到特定限制时具有主超时f()
。但是,我认为这并不实用,您应该首先解决无限循环。
在 Posix 系统(Linux、MacOS)上,您可以在调用函数之前使用setitimer()
安排将来的警报。信号 SIGALRM 将在指定的延迟后传送到进程。确保您的程序具有信号处理程序,您应该在启动计时器之前向sigaction()
注册它。
当信号处理程序在信号引发后进行控制时,如果违规循环带有setjmp()
和longjmp()
,您可能会退出。
如果您以显示的方式调用f()
(从main
),那么此时主要上下文在f
,而不是main
,因此您无法"从main
检查f
"。
您可以尝试从单独的线程调用f()
,并检查该线程是否已在指定的时间限制内完成。但是,我不确定这是否实用。虽然我不知道您真正打算在该函数中做什么,但在某些情况下,您可能会在需要清理的位置停止执行此函数。我想到的一个例子是它调用malloc
但能够在您中断它的地方调用free
。
老实说,如果对给定函数必须完成的时间有一定的要求,只需将该检查放在函数本身中并返回false
以指示它未成功完成。