Search code examples
cvisual-studio-codestdoutstdinstderr

Why the output of this program is unordered and written to stdout?


In this program, the output is unordered and as stdout is linked to the terminal the output of fprintf() containing stdout is written to the terminal, but why the output of the fprintf() containing stderr also printed to the terminal? Rather it should be printed to the stderr file handle. Is stderr also linked to the terminal?

CODE

#include <stdio.h>
#include <unistd.h>

int main() {
    fprintf(stdout, "This is to stdout. ");
    fprintf(stderr, "This is to stderr. ");
    fprintf(stdout, "This is also to stdout. ");
    return 0;
}

OUTPUT (in online compiler)

This is to stderr. This is to stdout. This is also to stdout.

Also I would like to ask one thing. Why the output is different in my Visual Studio Code IDE.

VS CODE OUTPUT

This is to stdout. This is to stderr. This is also to stdout.

Here VS CODE gives different output from the online compiler. Why it is so and how to fix this?


Solution

  • In your C implementation, when output is to a terminal, stdout is line buffered, and stderr is unbuffered. This means that output to stderr is sent to the terminal immediately, whereas output to stdout is sent only when:

    • a new-line character is sent,
    • input is requested from the user1 (so a prompt sent on stdout will appear before the user is supposed to type a response),
    • the buffer is full, or
    • a buffer flush is requested (as with fflush).

    When a stream is buffered, characters may be accumulated in a buffer and sent or received as a block when the buffer is full. Buffering is used because the operations to send or receive data have a large overhead, so executing them for each character sent, or each partial line, would be expensive in terms of resources such as execution time and energy. Sending many characters from a buffer in one operation is more efficient.

    In addition to unbuffered and line buffered, there is also fully buffered, in which characters are intended to be sent or received only when the buffer is full. The C standard describes buffering in C 2018 7.21.3 3.

    In 7.21.3 7, the standard tells us the standard error stream is not fully buffered. It may be unbuffered or line buffered. Your C implementation apparently chooses to make it unbuffered, so error messages will be written to the terminal immediately. That paragraph also tells us that standard input and standard output are fully buffered only if the stream “can be determined not to refer to an interactive device.” For example, if you redirect output to a file instead of the terminal, it does not refer to an interactive device, and it should be fully buffered. When output is known to be going to an interactive device, it should not be fully buffered. It may be line buffered or unbuffered, and your C implementation apparently chooses to make it line buffered.

    If you modify the program to include \n at the end of each string it prints, you will see them in order, because the unbuffered and line buffered streams will print each complete line (characters ending with a new-line character) in order.

    You can also use setvbuf to change the buffering mode of a stream after it is open and before any other operation is performed on it. For example, to set stdout to unbuffered, you can use:

    int result = setvbuf(stdout, NULL, _IONBF, 0);
    if (result != 0)
        fprintf(stderr, "Error, setvbuf returned %d.\n", result);
    

    setvbuf is specified in C 2018 7.21.5.6. _IOFBF requests fully buffered, and _IOLBF requests line buffered. (The second and fourth parameters are for providing your own array to use as the buffer and its length.)

    Footnote

    1 Specifically, requesting input (as by calling fgetc or scanf) on an unbuffered stream or on a line buffered stream that “requires the transmission of characters from the host environment” will trigger flushing of line buffered output.