Working with standard input/output streams is a cornerstone of programming in C.
Background
The first implementation of stdio in C (through <stdio.h>) involved three standard streams: stdin (standard input), stdout (standard output), and stderr (standard error stream). These streams allow writing portable code for interacting with users and automation tools.
Problem
Not everyone knows the subtleties: streams can be redirected, buffering differs, erroneous handling of buffers or the order of closing them can lead to unexpected crashes and data loss.
Solution
All standard input/output functions, by default, work with stdin, stdout, or stderr. They can be redirected by the operating system (for example, using a command in the shell) as well as within the program itself — through freopen or setvbuf for managing buffering.
Example code (redirecting stdout to a file):
#include <stdio.h> int main() { FILE *fp = freopen("output.txt", "w", stdout); if (!fp) { perror("freopen failed"); return 1; } printf("This will go into the file output.txt! "); fclose(fp); // Close it! Might need to explicitly close stdout return 0; }
Key features:
Can you pass the descriptors of stdin, stdout, or stderr to other processes and do anything with them?
Only if the OS supports descriptor inheritance (for example, in Unix through fork), but this is not always correct, especially when mixing low-level I/O (read/write) and stdio (fgets/printf) - buffer inconsistency may occur.
Is it necessary to manually flush stdout and stderr?
For stdout, flushing (fflush) is needed if you need to ensure that data is written immediately (for example, before an emergency exit). stderr is usually not buffered; its output goes immediately.
What will happen if you don’t close freopen-ed stdout?
Part of the data may be lost due to an unflushed buffer! It's important to explicitly close the stream (fclose) or do fflush(stdout) before the program ends.
Example code:
fclose(stdout); // will flush the buffer and close the stream
Pros: Unified interface, buffering for speed, easy output replacement during testing.
Cons: Data loss with forgotten fflush/fclose, confusion when mixing stdio and low-level io, loss of error visibility with redirected stdout and stderr.
Negative case: A testing utility overrides stdout and then forgets to close the stream - 20% of results in the file are lost. Pros: nothing needs to be changed in the rest of the code, cons: data loss and difficult diagnosis.
Positive case: The program outputs reports to stdout and errors to stderr; for debugging, stderr always goes immediately (without buffering), and after completing the reporting block, fflush(stdout) is called. Pros: quick response to errors, reliable report writing; cons: requires discipline in handling buffers.