Comment attraper Ctrl+ Cen C?
Comment attraper Ctrl+ Cen C?
Réponses:
Avec un gestionnaire de signaux.
Voici un exemple simple de retournement d'un bool
utilisé dans main()
:
#include <signal.h>
static volatile int keepRunning = 1;
void intHandler(int dummy) {
keepRunning = 0;
}
// ...
int main(void) {
signal(SIGINT, intHandler);
while (keepRunning) {
// ...
Edit en juin 2017 : à qui cela peut concerner, en particulier ceux qui ont une envie insatiable de modifier cette réponse. Écoutez, j'ai écrit cette réponse il y a sept ans. Oui, les normes linguistiques changent. Si vous devez vraiment améliorer le monde, veuillez ajouter votre nouvelle réponse, mais laissez la mienne telle quelle. Comme la réponse porte mon nom, je préférerais qu'elle contienne aussi mes mots. Je vous remercie.
bool volatile keepRunning = true;
sûr à 100%. Le compilateur est libre de mettre keepRunning
en cache dans un registre et le volatile l'empêchera. En pratique, il peut très probablement également fonctionner sans le mot clé volatile lorsque la boucle while appelle au moins une fonction non en ligne.
sig_atomic_t
ou atomic_bool
taperais là-bas. Je viens de manquer celui-là. Maintenant, puisque nous parlons: voudriez-vous que j'annule ma dernière modification? Pas de rancune là-bas, ce serait parfaitement compréhensible de votre point de vue :)
Vérifiez ici:
Note: Évidemment, ceci est un exemple simple expliquant simplement comment mettre en place un CtrlCgestionnaire, mais comme toujours il y a des règles qui doivent être respectées afin de ne pas casser autre chose. Veuillez lire les commentaires ci-dessous.
L'exemple de code ci-dessus:
#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
est une bonne chose, mais gcc
et d'autres compilateurs le compilent en programmes fonctionnant correctement depuis les années 1990. Expliqué assez bien ici: eskimo.com/~scs/readings/voidmain.960823.html - c'est fondamentalement une "fonctionnalité", je le prends comme ça.
Addendum concernant les plates-formes UN * X.
Selon la signal(2)
page de manuel sur GNU / Linux, le comportement de signal
n'est pas aussi portable que celui de sigaction
:
Le comportement de signal () varie selon les versions UNIX et a également varié historiquement entre les différentes versions de Linux. Évitez son utilisation: utilisez plutôt sigaction (2).
Sur le système V, le système ne bloquait pas la livraison d'autres instances du signal et la livraison d'un signal réinitialisait le gestionnaire à la valeur par défaut. Dans BSD, la sémantique a changé.
La variante suivante de la réponse précédente de Dirk Eddelbuettel utilise à la sigaction
place 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 vous pouvez mettre le terminal en mode brut, comme ceci:
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);
Maintenant, il devrait être possible de lire Ctrl+ les Cfrappes avec fgetc(stdin)
. Attention cependant à utiliser ceci car vous ne pouvez pas non plus Ctrl+ Z, Ctrl+ Q, Ctrl+ S, etc. comme normalement.
Configurez un piège (vous pouvez intercepter plusieurs signaux avec un seul gestionnaire):
signal (SIGQUIT, mon_handler); signal (SIGINT, mon_handler);
Gérez le signal comme vous le souhaitez, mais soyez conscient des limites et des pièges:
void my_handler (int sig) { / * Votre code ici. * / }
@Peter Varo a mis à jour la réponse de Dirk, mais Dirk a rejeté le changement. Voici la nouvelle réponse de Peter:
Bien que l'extrait de code ci-dessus soit correct c89Par exemple, on devrait utiliser les types les plus modernes et les garanties fournies par les normes ultérieures si possible. Par conséquent, voici une alternative plus sûre et moderne pour ceux qui recherchent lec99 et c11 mise en œuvre 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;
}
C11 Standard: 7.14§2 L'en-tête
<signal.h>
déclare un type ...sig_atomic_t
qui est le type entier (éventuellement volatile) d'un objet auquel on peut accéder en tant qu'entité atomique, même en présence d'interruptions asynchrones.
En outre:
C11 Norme: 7.14.1.1§5 Si le signal se produit autrement qu'à la suite de l'appel de la fonction
abort
ouraise
, le comportement est indéfini si le gestionnaire de signal fait référence à un objet avecstatic
une durée de stockage de thread ou qui n'est pas un objet atomique sans verrouillage autre qu'en attribuant une valeur à un objet déclaré commevolatile sig_atomic_t
...
(void)_;
... Quel est son but? Est-ce que le compilateur n'avertit pas d'une variable inutilisée?
En ce qui concerne les réponses existantes, notez que la gestion du signal dépend de la plate-forme. Win32, par exemple, gère beaucoup moins de signaux que les systèmes d'exploitation POSIX; voir ici . Alors que SIGINT est déclaré dans signaux.h sur Win32, consultez la note dans la documentation qui explique qu'il ne fera pas ce que vous pourriez attendre.
#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;
}
La fonction sig_handler vérifie si la valeur de l'argument passé est égale au SIGINT, puis le printf est exécuté.
Ceci s'imprime juste avant de quitter.
#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);
}