Skip to content

C Input & Output

Input refers to providing data to a program, typically from the keyboard, files, or network connections. Output refers to presenting data from a program, usually to the screen, files, or network.

C provides a standard library (stdio.h) with functions for performing input and output (I/O) operations.

When a C program starts, three standard I/O streams are automatically opened:

Stream NameFile PointerTypical Device & Purpose
Standard InputstdinTypically connected to the keyboard. Used for reading input.
Standard OutputstdoutTypically connected to the screen/terminal. Used for displaying normal output.
Standard ErrorstderrTypically connected to the screen/terminal. Used for displaying error messages.

These streams are represented by file pointers (FILE *), providing a consistent interface for interacting with these standard devices.

These functions handle I/O one character at a time.

  • int getchar(void): Reads the next available character from stdin and returns it as an int. Returns EOF (a special integer constant, usually -1) on end-of-file or error.

  • int putchar(int c): Writes the character c (converted to unsigned char) to stdout and returns the character written, or EOF on error.

Example:

#include <stdio.h>
int main(void) {
int c;
printf("Enter a character: ");
c = getchar(); // Read one character
// Check if getchar() succeeded before using putchar()
if (c != EOF) {
printf("You entered: ");
putchar(c); // Write the character back
putchar('\n'); // Add a newline for better formatting
} else {
printf("\nEnd of input or error encountered.\n");
}
return 0;
}

When run, this program waits for input. If you type hello and press Enter, it reads only the first character ‘h’:

Enter a character: hello
You entered: h

These functions handle I/O one line at a time.

  • char *fgets(char *str, int size, FILE *stream): Reads a line from the specified stream (e.g., stdin) into the buffer str. It stops reading after size - 1 characters, or when a newline character (\n) is read, or when end-of-file is reached. Crucially, fgets is safe because it prevents buffer overflows by respecting the provided size. It stores the newline character if read and space permits, and always null-terminates the string (\0). Returns str on success, NULL on error or end-of-file without reading any characters.

  • int puts(const char *s): Writes the string s to stdout, followed by a newline character. Returns a non-negative value on success, or EOF on error.

Important Security Note: Avoid using the legacy gets() function. It does not perform bounds checking and can easily lead to buffer overflows, a serious security vulnerability. Always use fgets() instead of gets() for reading strings.

Example using fgets and puts:

#include <stdio.h>
#include <string.h> // For strcspn, if removing newline
#define BUFFER_SIZE 100
int main(void) {
char str[BUFFER_SIZE];
printf("Enter a line of text: ");
// Read a line safely using fgets
if (fgets(str, BUFFER_SIZE, stdin) != NULL) {
// Optional: Remove the trailing newline character if fgets stored it
// strcspn finds the first occurrence of any character in the reject string (here, just newline)
str[strcspn(str, "\n")] = 0;
printf("You entered: ");
puts(str); // puts adds its own newline
} else {
// Handle error or EOF
printf("\nFailed to read input or reached end-of-file.\n");
}
return 0;
}

When run:

Enter a line of text: This is a test line.
You entered: This is a test line.

These functions handle I/O according to specified format strings, allowing you to read and write various data types.

  • int scanf(const char *format, ...): Reads formatted input from stdin according to the format string. Subsequent arguments are pointers to variables where the read data should be stored. Returns the number of input items successfully matched and assigned, or EOF on input failure before any conversion.

  • int printf(const char *format, ...): Writes formatted output to stdout according to the format string. Subsequent arguments are the values to be formatted and printed. Returns the number of characters printed, or a negative value on error.

The format string contains conversion specifiers (e.g., %d for int, %f for float/double, %c for char, %s for string, %zu for size_t) that determine how arguments are converted. Both functions have many formatting options.

Example:

#include <stdio.h>
#define MAX_NAME_LEN 50
int main(void) {
char name[MAX_NAME_LEN];
int age;
int items_read;
printf("Enter your name and age (e.g., Alice 30): ");
// Read a string (up to whitespace) and an integer
// Use width limit for %s to prevent buffer overflow
// Check the return value of scanf!
items_read = scanf("%49s %d", name, &age); // %49s reads max 49 chars + null terminator
if (items_read == 2) { // Check if both items were successfully read
printf("\nHello, %s! You are %d years old.\n", name, age);
} else if (items_read == 1) {
printf("\nError: Only read the name. Please enter name and age correctly.\n");
} else {
// Could be EOF or matching failure
printf("\nError: Failed to read input correctly.\n");
// It's good practice to clear the input buffer here if retrying input
int c;
while ((c = getchar()) != '\n' && c != EOF) { /* consume rest of line */ }
}
return 0;
}

When run:

Enter your name and age (e.g., Alice 30): Bob 25
Hello, Bob! You are 25 years old.

Important Notes on scanf:

  • Whitespace Sensitivity: Most format specifiers (%d, %s, %f, etc.) automatically skip leading whitespace. However, %c does not skip whitespace.

  • %s Stops at Whitespace: The %s specifier reads characters until it encounters any whitespace (space, tab, newline). If you need to read a full line containing spaces, use fgets.

  • Security: Using %s without a width limit (%49s in the example) is dangerous as it can cause buffer overflows if the input is longer than the buffer. Always specify a maximum width or use fgets.

  • Return Value: Always check the return value of scanf to ensure the expected number of items were successfully read and assigned. Input might not match the format string, leading to errors if not checked.

  • Input Buffer: If scanf fails to match, the problematic input often remains in the input buffer, potentially causing issues for subsequent reads. Techniques exist to clear the buffer when needed.

  • Prefer fgets to read entire lines of input into a buffer first. This gives you more control and avoids many scanf pitfalls.

  • After reading a line with fgets, you can parse the buffer using sscanf (the string version of scanf), which offers the same formatting capabilities but reads from a string instead of stdin. This often leads to more robust input handling.

  • Always check the return values of I/O functions (fgets, scanf, printf, putchar, getchar, etc.) to detect errors or unexpected conditions (like end-of-file).

  • Use stderr for printing error messages, as stdout might be redirected.

For detailed information on format specifiers and advanced I/O functions, consult C documentation or resources like the printf(3) and scanf(3) man pages.