4

I have this simple piece of code in a file named printftest.c.

#include <stdio.h>

int main(){
    int c, i;

    i = 0;

    while ((c = getchar())  != EOF)
        ++i;

    printf("c = %d\n", c);
    printf("i = %d\n", i);
}

Compilation and execution is done as follows (on Windows): gcc printftest.c && a.exe

Terminal session looks like this:

gcc printftest.c && a.exe
c = -1
^C

Now when I give ctrl-c (keyboard interrupt) as input in the terminal only the first printf statement is executed. Why does this happen? I would expect to print both of the statements or none. Can anyone explain where execution stops exactly and why?

BramAppel
  • 1,346
  • 1
  • 9
  • 21
  • What compiler version? – klutt Nov 05 '19 at 09:41
  • Type Ctrl+Z (Ctrl+D on linux) to send the EOF signal to your program – pmg Nov 05 '19 at 09:42
  • Show an example with the input and exactly what output – klutt Nov 05 '19 at 09:43
  • 1
    @klutt gcc version 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project) – BramAppel Nov 05 '19 at 09:53
  • A MSVC compilation outputs `i = 0` as well (both lines) after pressing Ctrl-C. But the `^C` is not always output (Windows 7). Actually I am surprised that anything is output at all, because Ctrl-C is an instruction to end the program not the input. – Weather Vane Nov 05 '19 at 09:55
  • @pmg A normal exit occurs when Ctrl-Z (both printf statements are executed) is given but only one is executed when Ctrl-C is given. – BramAppel Nov 05 '19 at 09:56
  • It is because the Ctrl-C detection is asynchronous. A simple program with an infinite loop and no I/O is stopped by Ctrl-C, so it has nothing to do with the `getchar` or `printf` statements. Ctrl-C causes program termination irrespective of what is being output at the time. But it is an unusual way to terminate a program so perhaps has a low priority. – Weather Vane Nov 05 '19 at 10:12
  • It is a threading race, how much output you'll see is unpredictable. The Ctrl+C condition is signaled by a threadpool thread, docs [are here](https://learn.microsoft.com/en-us/windows/console/setconsolectrlhandler). If you don't use it then the default handler takes care of terminating the process. – Hans Passant Nov 05 '19 at 10:43

1 Answers1

3

Note, this is how it works on Linux. Windows has something similar. It's not exactly the same, but the basic principle is quite alike. Read more about how it's handled in Windows here: https://learn.microsoft.com/en-us/windows/console/setconsolectrlhandler

When you invoke C-C, the kernel will send a SIGINT to the program. Unless this signal is handled by the program, it will be killed.

This is done asynchronously. It basically looks like this:

1) User: C-C
2) Kernel: Hey program, do you hear me? I will kill you if you don't answer.
3) Kernel: The program does not seem to hear me. Let's kill it. 

What happens between 2 and 3 is a bit of a gamble. A SIGINT is usually used to say to the program that the user have been tired of waiting for whatever the program is doing. The kernel will only forcefully kill the program if it does not handle the SIGINT, but it has no requirements on how to handle it.

If you want the behavior to be well defined, you have to install a handler. Here is an example of how you can modify your program (in Linux) to ignore C-C completely:

#include <signal.h>

void install_handler()
{
        static struct sigaction sigact = { 0 };
        sigact.sa_handler = SIG_IGN;
        sigaction(SIGINT, &sigact, NULL);
}

int main()
{
        install_handler()

        // Your code
klutt
  • 30,332
  • 17
  • 55
  • 95
  • So the fact that the first printf statement is executed has nothing to do with C-C having the same value as EOF but with the fact that the termination behaviour is undefined? – BramAppel Nov 05 '19 at 11:10
  • @BramAppel Exactly. If you want a defined behavior you need to install a handler. – klutt Nov 05 '19 at 11:12
  • 1
    How does this explain why OP sees output from `printf("c = %d\n", c);` and not from `printf("i = %d\n", i);`? Press Control-C while the program is paused for `getchar` ought to terminate it before the first `printf` does anything. And, if it does not, it is unlikely termination would come after the first `printf` and before the second. – Eric Postpischil Aug 10 '23 at 01:21