Wie fängt man Ctrl+ Cin C?
Wie fängt man Ctrl+ Cin C?
Antworten:
Mit einem Signalhandler.
Hier ist ein einfaches Beispiel für das Umdrehen eines bool
verwendeten in main()
:
#include <signal.h>
static volatile int keepRunning = 1;
void intHandler(int dummy) {
keepRunning = 0;
}
// ...
int main(void) {
signal(SIGINT, intHandler);
while (keepRunning) {
// ...
Bearbeiten im Juni 2017 : Wen es betrifft, insbesondere diejenigen mit einem unersättlichen Drang, diese Antwort zu bearbeiten. Schau, ich habe diese Antwort vor sieben Jahren geschrieben. Ja, Sprachstandards ändern sich. Wenn Sie die Welt wirklich verbessern müssen, fügen Sie bitte Ihre neue Antwort hinzu, aber lassen Sie meine so wie sie ist. Da die Antwort meinen Namen trägt, würde ich es vorziehen, wenn sie auch meine Worte enthält. Danke dir.
bool volatile keepRunning = true;
100% sicher sein. Der Compiler kann frei keepRunning
in einem Register zwischengespeichert werden, und das Flüchtige verhindert dies. In der Praxis funktioniert es höchstwahrscheinlich auch ohne das flüchtige Schlüsselwort, wenn die while-Schleife mindestens eine Nicht-Inline-Funktion aufruft.
sig_atomic_t
oder atomic_bool
tippen. Ich habe das gerade verpasst. Nun, da wir uns unterhalten: Möchten Sie, dass ich meine letzte Bearbeitung zurücksetze? Keine harten Gefühle dort wäre es aus Ihrer Sicht vollkommen verständlich :)
Überprüfe hier:
Hinweis: Offensichtlich ist dies ein einfaches Beispiel zu erklären nur , wie ein einzurichtenCtrlC Handler, aber wie immer gibt es Regeln, die Notwendigkeit , um befolgt werden , nicht etwas anderes zu brechen. Bitte lesen Sie die Kommentare unten.
Der Beispielcode von oben:
#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
ist eine richtige Sache, aber gcc
und andere Compiler kompilieren dies zu einem korrekt laufenden Programm seit den 1990er Jahren. Hier ziemlich gut erklärt: eskimo.com/~scs/readings/voidmain.960823.html - es ist im Grunde eine "Funktion", ich nehme es so.
Nachtrag zu UN * X-Plattformen.
Laut der signal(2)
Manpage unter GNU / Linux ist das Verhalten von signal
nicht so portabel wie das Verhalten von sigaction
:
Das Verhalten von signal () variiert je nach UNIX-Version und hat sich auch historisch zwischen verschiedenen Linux-Versionen geändert. Vermeiden Sie die Verwendung: Verwenden Sie stattdessen Sigaction (2).
Auf System V blockierte das System die Zustellung weiterer Instanzen des Signals nicht, und die Zustellung eines Signals würde den Handler auf den Standardwert zurücksetzen. In BSD hat sich die Semantik geändert.
Die folgende Variation der vorherigen Antwort von Dirk Eddelbuettel verwendet sigaction
anstelle von 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
}
}
Oder Sie können das Terminal wie folgt in den Raw-Modus versetzen:
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);
Jetzt sollte es möglich sein, Ctrl+ CTastenanschläge mit zu lesen fgetc(stdin)
. Passen Sie auf, dass Sie dies nicht verwenden , da Sie Ctrl+ Z, Ctrl+ Q, Ctrl+ Susw. auch nicht mehr normal mögen können.
Richten Sie eine Falle ein (Sie können mehrere Signale mit einem Handler abfangen):
Signal (SIGQUIT, my_handler); Signal (SIGINT, my_handler);
Behandeln Sie das Signal so, wie Sie möchten, aber beachten Sie die Einschränkungen und Fallstricke:
void my_handler (int sig) { / * Dein Code hier. * / }}
@ Peter Varo hat Dirks Antwort aktualisiert, aber Dirk hat die Änderung abgelehnt. Hier ist die neue Antwort von Peter:
Obwohl das obige Snippet korrekt ist c89Beispielsweise sollte man nach Möglichkeit die moderneren Typen und Garantien verwenden, die durch die späteren Normen bereitgestellt werden. Daher ist hier eine sicherere und modernere Alternative für diejenigen, die nach dem suchenc99 und c11 konforme Umsetzung:
#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 Der Header
<signal.h>
deklariert einen Typ ...,sig_atomic_t
der der (möglicherweise flüchtig qualifizierte) Integer-Typ eines Objekts ist, auf den auch bei asynchronen Interrupts als atomare Entität zugegriffen werden kann.
Außerdem:
C11 Standard: 7.14.1.1§5 Wenn das Signal nicht als Ergebnis des Aufrufs der Funktion
abort
oder auftrittraise
, ist das Verhalten undefiniert, wenn der Signalhandler auf ein Objekt mitstatic
oder eine Thread-Speicherdauer verweist, das kein anderes sperrenfreies atomares Objekt ist als indem Sie einem alsvolatile sig_atomic_t
... deklarierten Objekt einen Wert zuweisen.
(void)_;
... Was ist ihr Zweck? Ist es so, dass der Compiler nicht vor einer nicht verwendeten Variablen warnt?
Beachten Sie in Bezug auf vorhandene Antworten, dass die Signalverarbeitung plattformabhängig ist. Win32 verarbeitet beispielsweise weit weniger Signale als POSIX-Betriebssysteme. siehe hier . Während SIGINT in wins.h unter Win32 deklariert ist, lesen Sie den Hinweis in der Dokumentation, in dem erklärt wird, dass es nicht das tut, was Sie vielleicht erwarten.
#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;
}
Die Funktion sig_handler prüft, ob der Wert des übergebenen Arguments gleich dem SIGINT ist, und führt dann printf aus.
Dies wird nur vor dem Beenden gedruckt.
#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);
}