Answers:
带有信号处理程序。
这是一个简单的示例,bool
用于翻转中的main()
:
#include <signal.h>
static volatile int keepRunning = 1;
void intHandler(int dummy) {
keepRunning = 0;
}
// ...
int main(void) {
signal(SIGINT, intHandler);
while (keepRunning) {
// ...
编辑于2017年6月:可能关注的对象,尤其是那些渴望编辑此答案的人。看,我七年前写了这个答案。是的,语言标准在变化。如果您真的必须改善世界,请添加您的新答案,但不要更改。因为答案上面有我的名字,所以我也希望它也包含我的话。谢谢。
bool volatile keepRunning = true;
100%安全的。编译器可以自由缓存keepRunning
在寄存器中,而volatile将阻止这种情况。实际上,当while循环调用至少一个非内联函数时,很可能在没有volatile关键字的情况下也可以工作。
sig_atomic_t
或atomic_bool
键入。我只是想念那个。现在,由于我们正在谈论:您是否希望我回滚我的最新编辑?从您的角度来看,没有难过的感觉是完全可以理解的:)
在这里检查:
注意:显然,这是一个简单的示例,仅说明了如何设置CtrlC处理程序,但是与往常一样,为了不破坏其他内容,需要遵守一些规则。请阅读下面的评论。
上面的示例代码:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void INThandler(int);
int main(void)
{
signal(SIGINT, INThandler);
while (1)
pause();
return 0;
}
void INThandler(int sig)
{
char c;
signal(sig, SIG_IGN);
printf("OUCH, did you hit Ctrl-C?\n"
"Do you really want to quit? [y/n] ");
c = getchar();
if (c == 'y' || c == 'Y')
exit(0);
else
signal(SIGINT, INThandler);
getchar(); // Get new line character
}
int main
是正确的做法,但gcc
自1990年代以来,其他编译器将此编译为可正确运行的程序。在这里很好的解释了:eskimo.com/~scs/readings/voidmain.960823.html-它基本上是一个“功能”,我这样认为。
有关UN * X平台的附录。
根据signal(2)
GNU / Linux上的手册页,的行为signal
不如的行为可移植sigaction
:
signal()的行为在UNIX版本之间有所不同,并且在历史上在Linux的不同版本中也有所不同。避免使用它:改为使用sigaction(2)。
在系统V上,系统未阻止其他信号实例的传递,并且信号的传递会将处理程序重置为默认实例。在BSD中,语义发生了变化。
以下是Dirk Eddelbuettel先前回答的变体,sigaction
而不是使用signal
:
#include <signal.h>
#include <stdlib.h>
static bool keepRunning = true;
void intHandler(int) {
keepRunning = false;
}
int main(int argc, char *argv[]) {
struct sigaction act;
act.sa_handler = intHandler;
sigaction(SIGINT, &act, NULL);
while (keepRunning) {
// main loop
}
}
或者,您可以将终端置于原始模式,如下所示:
struct termios term;
term.c_iflag |= IGNBRK;
term.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
term.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(fileno(stdin), TCSANOW, &term);
现在应该可以使用读取Ctrl+ C击键fgetc(stdin)
。不过要小心使用它,因为您不能再像往常一样Ctrl+ Z,Ctrl+ Q,Ctrl+ S等等。
@Peter Varo更新了Dirk的答案,但Dirk拒绝了更改。这是彼得的新答案:
尽管以上代码段是正确的 c89例如,如果可能的话,应该使用更高标准的类型和更高版本所提供的保证。因此,对于那些寻求c99 和 c11 符合要求的实现:
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
static volatile sig_atomic_t keep_running = 1;
static void sig_handler(int _)
{
(void)_;
keep_running = 0;
}
int main(void)
{
signal(SIGINT, sig_handler);
while (keep_running)
puts("Still running...");
puts("Stopped by signal `SIGINT'");
return EXIT_SUCCESS;
}
C11标准:7.14§2标头
<signal.h>
声明一个类型...sig_atomic_t
,它是对象的(可能是易失性限定)整数类型,即使存在异步中断,该对象也可以作为原子实体进行访问。
此外:
C11标准:7.14.1.1§5如果信号不是通过调用
abort
或raise
函数产生的,则信号处理程序引用的对象具有非static
线程锁对象或线程存储持续时间不是对象时,则行为未定义而不是通过为声明为volatile sig_atomic_t
... 的对象赋值
(void)_;
...的目的是什么?这样编译器就不会警告未使用的变量吗?
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signo)
{
if (signo == SIGINT)
printf("received SIGINT\n");
}
int main(void)
{
if (signal(SIGINT, sig_handler) == SIG_ERR)
printf("\ncan't catch SIGINT\n");
// A long long wait so that we can easily issue a signal to this process
while(1)
sleep(1);
return 0;
}
函数sig_handler检查所传递的参数的值是否等于SIGINT,然后执行printf。
这只是在退出前打印。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void sigint_handler(int);
int main(void)
{
signal(SIGINT, sigint_handler);
while (1){
pause();
}
return 0;
}
void sigint_handler(int sig)
{
/*do something*/
printf("killing process %d\n",getpid());
exit(0);
}