我必须在 while 循环中获取用户的输入,然后采取一些操作。我还想通过 ctrl+c 输入退出我的代码。
void my_signal_handler(int sig)
{
running = false;
signal(sig, SIG_IGN);
}
int main(void)
{
struct sigaction sa = {{0}};
sa.sa_handler = &my_signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGINT, &sa, NULL) != 0)
{
fprintf(stderr, "sigaction error\n");
return -1;
}
while(running)
{
printf("enter number: ");
scanf("%d", &num);
// take action based on number
}
}
此代码的问题是按 ctrl+c 后,它不会退出,但会等待 scanf 的输入。因此,一旦我按下额外的键,程序就会退出(信号处理程序被调用)。
如何删除按 ctrl+c 后向 scanf 提供输入的这个额外步骤?
最佳答案
您应该安装信号处理程序。至少,添加
signal(SIGINT, my_signal_handler);
在你的main
之前while(running)
,但最好使用sigaction(2) .
您还应该知道 stdio 是 buffering ;通常,当 stdout 是终端时,它是行缓冲的(但请参阅 setvbuf(3) 和 friend )。所以你应该调用 fflush(3) (可能如 fflush(NULL);
)在循环内的 scanf
之前,或使用显式终止每个 printf
格式控制字符串 \n
.
最后,scanf(3)可能会失败并返回已扫描项目的数量,您应该对此进行测试。
顺便说一句,您的 main
是错误的,应定义为 int main(void)
或最好 int main(int argc, char**argv)
.
但是(假设您使用的是 Linux),请仔细阅读 signal(7) (注意关于信号处理程序和异步信号安全函数的说法)和 POSIX signal.h
documentation并将您的 running
标志声明为
volatile sigatomic_t running;
(或者,在 C11 中,作为 volatile _Atomic bool running;
)
volatile
qualifier非常重要。否则,允许编译器进行优化(例如,假设running
始终为真)。
请注意,使用 signal(2)通常是一个坏主意。首先,如果你确实需要信号处理,你最好使用 sigaction(2) 。那么您对 signal(sig, SIG_IGN);
的调用在您的情况下是无用的(因为 running
volatile 标志将在信号处理程序中更改)。最后,对于多路复用输入(和输出),您可以使用 poll(2)它可用于等待并测试 stdin
上是否有可用输入(实际上 STDIN_FILENO
为 0),更一般地用于实现 event loops 。您可以使用(而不是我强烈推荐的 poll
)旧的且几乎过时的 select(2) ,但您更愿意使用 poll(2) 。另请参阅epoll(7) & inotify(7)但您可能不需要它们。
请注意,在终端中,stdin
通常是一个 tty(使用 isatty(3) 检查),后面是 line discipline。 (因此一些行缓冲发生在内核内部)。阅读tty demystified页。考虑使用GNU readline库和函数(或者可能 ncurses ),这可能是您真正需要的。
另请阅读Advanced Linux Programming并养成阅读您正在使用的每个函数的文档的习惯。
关于c - 在循环中使用 scanf 处理 SIGINT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38069818/