Como se pega Ctrl+ Cem C?
Como se pega Ctrl+ Cem C?
Respostas:
Com um manipulador de sinal.
Aqui está um exemplo simples lançando um bool
usado em main()
:
#include <signal.h>
static volatile int keepRunning = 1;
void intHandler(int dummy) {
keepRunning = 0;
}
// ...
int main(void) {
signal(SIGINT, intHandler);
while (keepRunning) {
// ...
Editar em junho de 2017 : a quem possa interessar, principalmente aqueles com um desejo insaciável de editar esta resposta. Olha, eu escrevi esta resposta há sete anos. Sim, os padrões de idioma mudam. Se você realmente deve melhorar o mundo, adicione sua nova resposta, mas deixe a minha como está. Como a resposta tem meu nome, eu prefiro que contenha minhas palavras também. Obrigado.
bool volatile keepRunning = true;
ser 100% seguro. O compilador é livre para armazenar keepRunning
em cache em um registro e o volátil evitará isso. Na prática, é provável que também funcione sem a palavra-chave volátil quando o loop while chama pelo menos uma função não-inline.
sig_atomic_t
ou atomic_bool
digitaria lá. Eu apenas perdi essa. Agora, já que estamos conversando: você gostaria que eu revelasse minha edição mais recente? Sem ressentimentos não seria perfeitamente compreensível a partir de seu ponto de vista :)
Verifique aqui:
Nota: Obviamente, este é um exemplo simples que explica exatamente como configurar um CtrlCmanipulador, mas como sempre, existem regras que precisam ser obedecidas para não quebrar outra coisa. Por favor, leia os comentários abaixo.
O código de exemplo acima:
#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
é uma coisa apropriada, mas gcc
e outros compiladores compilam isso para programas em execução correta desde os anos 90. Explicou muito bem aqui: eskimo.com/~scs/readings/voidmain.960823.html - é basicamente um "recurso", eu entendo assim.
Adendo referente às plataformas UN * X.
De acordo com a signal(2)
página do manual no GNU / Linux, o comportamento de signal
não é tão portátil quanto o comportamento de sigaction
:
O comportamento do sinal () varia entre as versões UNIX e também variou historicamente entre diferentes versões do Linux. Evite seu uso: use sigaction (2).
No Sistema V, o sistema não bloqueou a entrega de outras instâncias do sinal e a entrega de um sinal redefiniria o manipulador para o padrão. No BSD, a semântica mudou.
A seguinte variação da resposta anterior de Dirk Eddelbuettel usa em sigaction
vez de 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
}
}
Ou você pode colocar o terminal no modo bruto, assim:
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);
Agora deve ser possível ler Ctrl+ Cpressionamentos de teclas usando fgetc(stdin)
. Cuidado com isso, porque você não pode Ctrl+Z , Ctrl+ Q, Ctrl+ S, etc.
Configure uma interceptação (você pode interceptar vários sinais com um manipulador):
sinal (SIGQUIT, my_handler); sinal (SIGINT, my_handler);
Manipule o sinal da maneira que desejar, mas esteja ciente das limitações e dicas:
void my_handler (int sig) { / * Seu código aqui. * / }
@ Peter Varo atualizou a resposta de Dirk, mas Dirk rejeitou a alteração. Aqui está a nova resposta de Peter:
Embora o snippet acima seja um correto c89Por exemplo, deve-se usar os tipos e garantias mais modernos fornecidos pelos padrões posteriores, se possível. Portanto, aqui está uma alternativa mais segura e moderna para aqueles que buscam oc99 e c11 implementação conforme:
#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;
}
Padrão C11: 7.14§2 O cabeçalho
<signal.h>
declara um tipo ...sig_atomic_t
que é o tipo inteiro (possivelmente qualificado para um volátil) de um objeto que pode ser acessado como uma entidade atômica, mesmo na presença de interrupções assíncronas.
Além disso:
C11 Standard: 7.14.1.1§5 Se o sinal ocorrer como resultado da chamada do
abort
raise
função ou , o comportamento será indefinido se o manipulador de sinal se referir a qualquer objeto comstatic
ou duração de armazenamento de encadeamento que não seja um objeto atômico livre de bloqueios do que atribuir um valor a um objeto declarado comovolatile sig_atomic_t
...
(void)_;
... Qual é o seu propósito? É assim que o compilador não avisa sobre uma variável não utilizada?
Em relação às respostas existentes, observe que o tratamento do sinal depende da plataforma. O Win32, por exemplo, lida com muito menos sinais que os sistemas operacionais POSIX; veja aqui . Enquanto o SIGINT é declarado em signs.h no Win32, consulte a nota na documentação que explica que ele não fará o que você poderia esperar.
#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;
}
A função sig_handler verifica se o valor do argumento passado é igual ao SIGINT, então o printf é executado.
Isso é impresso antes da saída.
#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);
}