Comment attrape-t-on Ctrl + C en C ?
D'accord, mais la question portait sur Ctrl-C et il répond à cette question - donc pas besoin de voter à la baisse.
Comment attrape-t-on Ctrl + C en C ?
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) {
// ...
Edition en juin 2017 : A qui de droit, en particulier ceux qui ont un besoin insatiable d'éditer cette réponse. Ecoutez, j'ai écrit cette réponse sept il y a des années. Oui, les normes linguistiques changent. Si vous voulez vraiment améliorer le monde, veuillez ajouter votre nouvelle réponse mais laissez le mien tel quel. Comme la réponse porte mon nom, je préfère qu'elle contienne aussi mes mots. Merci.
D'accord, mais la question portait sur Ctrl-C et il répond à cette question - donc pas besoin de voter à la baisse.
Mais je vais mettre à jour le code d'où je l'ai tiré, alors bravo. J'apprécie la correction.
@larsman : En fait, la norme C stipule que la possibilité d'attraper un signal dépend entièrement de l'implémentation. Il est donc concevable que sur une plateforme particulière, SIGKILL puisse être attrapé. C'est pourquoi je vote pour contrer votre vote négatif erroné.
Vérifiez ici :
Note : Évidemment, il s'agit d'un exemple simple expliquant juste comment mettre en place un CtrlC mais, comme toujours, il y a des règles à respecter pour 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
}
@Derrick D'accord, int main
est une chose correcte, mais gcc
et d'autres compilateurs le compilent en un programme fonctionnant correctement depuis les années 1990. Expliqué assez bien ici : eskimo.com/~scs/lectures/voidmain.960823.html - c'est essentiellement une "fonctionnalité", je le prends comme ça.
@icyrock.com : Tout cela est très vrai (en ce qui concerne void main() en C), mais lorsque l'on poste publiquement, il est probablement préférable d'éviter le débat en utilisant int main() de peur que cela ne détourne l'attention du point principal.
Il y a un énorme défaut à cela. Vous ne pouvez pas utiliser printf en toute sécurité dans le contenu d'un gestionnaire de signaux. C'est une violation de la sécurité des signaux asynchrones. Ceci est dû au fait que printf n'est pas réentrant. Que se passe-t-il si le programme était en train d'utiliser printf lorsque vous avez appuyé sur Ctrl-C, et que votre gestionnaire de signaux commence à l'utiliser au même moment ? Indice : il se cassera probablement. write et fwrite sont les mêmes à utiliser dans ce contexte.
Addendum concernant les plateformes UN*X.
Selon le signal(2)
de la page de manuel de GNU/Linux, le comportement de signal
n'est pas aussi portable que le comportement de sigaction
:
Le comportement de signal() varie d'une version d'UNIX à l'autre, et a également varié historiquement entre les différentes versions de Linux. Évitez son utilisation : utilisez sigaction(2) à la place.
Sur le Système V, le système ne bloquait pas la délivrance d'autres instances du signal et la délivrance d'un signal réinitialisait le gestionnaire à celui par défaut. Dans BSD, la sémantique a changé.
La variante suivante de la réponse précédente de Dirk Eddelbuettel utilise sigaction
au lieu 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 + C les frappes au clavier en utilisant fgetc(stdin)
. Attention cependant, car vous ne pouvez pas Ctrl + Z , Ctrl + Q , Ctrl + S etc. comme d'habitude non plus.
Mettez en place un piège (vous pouvez piéger plusieurs signaux avec un seul gestionnaire) :
signal (SIGQUIT, my\_handler);
signal (SIGINT, my\_handler);
Manipulez le signal comme vous le souhaitez, mais soyez conscient des limites et des pièges :
void my\_handler (int sig)
{
/\* Your code here. \*/
}
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
6 votes
La gestion des signaux n'existe pas en C...., du moins c'est ce que je pensais jusqu'à ce que je lise la norme C99. Il s'avère que la gestion des signaux est définie en C, mais que Ctrl-C n'est pas tenu de produire un signal spécifique ou un signal tout court. En fonction de votre plate-forme, cela peut être impossible.
1 votes
La gestion des signaux dépend principalement de l'implémentation. Sur les plateformes *nix, utilisez <signal.h>, et si vous êtes sous OSX, vous pouvez profiter de GCD pour rendre les choses encore plus faciles~.