Input and output (I/O) operations form the foundation of user interaction in C programs. From simple console interactions to file handling, understanding C's I/O system is essential for any programmer. This comprehensive guide covers everything from basic printf and scanf to advanced formatted I/O, providing you with the tools to handle any I/O scenario.
The Standard I/O Streams
C provides three standard streams that are automatically opened when a program starts:
| Stream | File Pointer | Purpose |
|---|---|---|
| Standard Input | stdin | Reading keyboard input |
| Standard Output | stdout | Normal program output |
| Standard Error | stderr | Error messages (unbuffered) |
#include <stdio.h>
int main() {
fprintf(stdout, "Normal output\n");
fprintf(stderr, "Error output\n");
return 0;
}
Basic Output with printf()
The printf() function is the most commonly used output function in C. It formats and prints data to stdout.
1. Format Specifiers
#include <stdio.h>
int main() {
// Integer types
int dec = 42;
printf("Decimal: %d\n", dec);
printf("Octal: %o\n", dec);
printf("Hexadecimal: %x (lowercase)\n", dec);
printf("Hexadecimal: %X (uppercase)\n", dec);
// Character and string
char ch = 'A';
char str[] = "Hello";
printf("Character: %c\n", ch);
printf("String: %s\n", str);
// Floating point
float f = 3.14159;
double d = 3.1415926535;
printf("Float: %f\n", f);
printf("Float (2 decimals): %.2f\n", f);
printf("Scientific: %e\n", d);
printf("Scientific (uppercase): %E\n", d);
printf("General: %g\n", d);
// Pointer
int x = 10;
printf("Pointer address: %p\n", (void*)&x);
// Size_t
size_t size = sizeof(int);
printf("Size: %zu\n", size);
// Unsigned types
unsigned int ui = 100;
printf("Unsigned: %u\n", ui);
// Long types
long l = 100000L;
long long ll = 10000000000LL;
printf("Long: %ld\n", l);
printf("Long long: %lld\n", ll);
return 0;
}
2. Format Specifier Modifiers
#include <stdio.h>
int main() {
int num = 123;
float pi = 3.14159;
// Width specification
printf("Width 5: |%5d|\n", num); // | 123|
printf("Width 5 (left): |%-5d|\n", num); // |123 |
// Precision
printf("Precision 2: %.2f\n", pi); // 3.14
printf("Precision 0: %.0f\n", pi); // 3
// Combined width and precision
printf("Width 8, prec 2: |%8.2f|\n", pi); // | 3.14|
printf("Left aligned: |%-8.2f|\n", pi); // |3.14 |
// Zero padding
printf("Zero pad 5: %05d\n", num); // 00123
// Sign display
printf("Positive: %+d\n", num); // +123
printf("Negative: %+d\n", -num); // -123
// Space for positive numbers
printf("Space: % d\n", num); // 123
// Alternative forms
printf("Hex: %#x\n", 255); // 0xff
printf("Octal: %#o\n", 255); // 0377
return 0;
}
3. Escape Sequences
#include <stdio.h>
int main() {
printf("Newline: First line\nSecond line\n");
printf("Tab:\tIndented text\n");
printf("Backslash: \\\n");
printf("Single quote: \'\n");
printf("Double quote: \"\n");
printf("Carriage return: First line\rOverwritten\n");
printf("Backspace: ABC\b\b\bDEF\n"); // DEF (ABC erased)
printf("Form feed: Page 1\fPage 2\n");
printf("Vertical tab: Line1\vLine2\vLine3\n");
printf("Alert (bell): \a\n"); // May produce sound
// Hexadecimal escape
printf("Hex character: \x41\n"); // 'A'
// Octal escape
printf("Octal character: \101\n"); // 'A'
return 0;
}
4. Advanced printf Features
#include <stdio.h>
int main() {
// Variable width and precision
int width = 10;
int precision = 3;
double value = 123.456789;
printf("Dynamic: |%*.*f|\n", width, precision, value); // | 123.457|
// Positional parameters (GNU extension)
printf("%2$s, %1$s\n", "World", "Hello"); // Hello, World
// Return value (number of characters printed)
int count = printf("Hello %s\n", "World");
printf("Printed %d characters\n", count);
// Using printf with string variable
char format[] = "Value: %d\n";
printf(format, 42);
return 0;
}
Basic Input with scanf()
The scanf() function reads formatted input from stdin.
1. Basic scanf Usage
#include <stdio.h>
int main() {
int age;
float height;
char name[50];
char initial;
printf("Enter your age: ");
scanf("%d", &age); // Note the & (address-of operator)
printf("Enter your height: ");
scanf("%f", &height);
printf("Enter your first initial: ");
scanf(" %c", &initial); // Space before %c consumes whitespace
printf("Enter your name: ");
scanf("%s", name); // No & needed for array
printf("Age: %d, Height: %.2f, Initial: %c, Name: %s\n",
age, height, initial, name);
return 0;
}
2. Multiple Inputs with scanf
#include <stdio.h>
int main() {
int a, b, c;
// Multiple values in one call
printf("Enter three numbers: ");
int count = scanf("%d %d %d", &a, &b, &c);
if (count == 3) {
printf("Sum: %d\n", a + b + c);
} else {
printf("Invalid input. Read %d values.\n", count);
}
// Mixed types
char name[30];
int id;
float gpa;
printf("Enter: ID Name GPA\n");
scanf("%d %s %f", &id, name, &gpa);
printf("ID: %d, Name: %s, GPA: %.2f\n", id, name, gpa);
return 0;
}
3. scanf Format Specifiers
#include <stdio.h>
int main() {
// Reading strings with width limit (prevents buffer overflow)
char buffer[20];
printf("Enter text (max 19 chars): ");
scanf("%19s", buffer);
// Reading until newline with scanset
char line[100];
printf("Enter a line: ");
scanf("%[^\n]", line); // Read until newline
// Reading only digits
char digits[50];
printf("Enter numbers only: ");
scanf("%[0-9]", digits);
// Reading hexadecimal
int hex;
printf("Enter hex number: ");
scanf("%x", &hex);
printf("Decimal: %d\n", hex);
// Reading with suppression (ignore input)
int year, month, day;
printf("Enter date (YYYY-MM-DD): ");
scanf("%d-%*d-%d", &year, &day); // Skip month
printf("Year: %d, Day: %d\n", year, day);
// Reading until a specific character
char word[50];
printf("Enter word (stop at space): ");
scanf("%[^ ]", word); // Read until space
return 0;
}
4. Scanset (Character Class) Examples
#include <stdio.h>
int main() {
char input[100];
// Read only alphabetic characters
printf("Enter alphabetic only: ");
scanf("%[A-Za-z]", input);
printf("Read: %s\n", input);
// Read alphanumeric characters
printf("Enter alphanumeric: ");
scanf("%[A-Za-z0-9]", input);
printf("Read: %s\n", input);
// Read any characters except newline and comma
printf("Enter text (no commas): ");
scanf("%[^,\n]", input);
printf("Read: %s\n", input);
// Read until a digit is encountered
printf("Enter text (stop at digit): ");
scanf("%[^0-9]", input);
printf("Read: %s\n", input);
// Clear input buffer after scanset
int c;
while ((c = getchar()) != '\n' && c != EOF);
return 0;
}
Character Input/Output
1. getchar() and putchar()
#include <stdio.h>
int main() {
// Simple character I/O
printf("Enter a character: ");
int ch = getchar(); // Returns int to handle EOF
printf("You entered: ");
putchar(ch);
putchar('\n');
// Reading until newline
printf("\nEnter a line (end with Enter): ");
while ((ch = getchar()) != '\n' && ch != EOF) {
putchar(ch);
}
putchar('\n');
// Count characters until EOF (Ctrl+D on Unix, Ctrl+Z on Windows)
printf("\nEnter text (Ctrl+D to end):\n");
int count = 0;
while ((ch = getchar()) != EOF) {
count++;
}
printf("Characters read: %d\n", count);
return 0;
}
2. getc() and putc()
#include <stdio.h>
int main() {
// getc() works like getchar() but can specify stream
printf("Enter a character: ");
int ch = getc(stdin);
printf("You entered: ");
putc(ch, stdout);
putc('\n', stdout);
// Using with files (will cover in file I/O)
return 0;
}
String Input/Output
1. gets() and puts() (Dangerous - Never use gets())
#include <stdio.h>
int main() {
// NEVER use gets() - it has no bounds checking!
// char buffer[10];
// gets(buffer); // DANGEROUS: buffer overflow possible
// Use fgets() instead
char line[50];
printf("Enter a line: ");
fgets(line, sizeof(line), stdin);
// Remove trailing newline if present
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n') {
line[len-1] = '\0';
}
// puts() automatically adds newline
puts(line);
// fputs() does NOT add newline
fputs("No newline", stdout);
fputs(" here\n", stdout);
return 0;
}
2. Safe String Input with fgets()
#include <stdio.h>
#include <string.h>
// Safe input function that handles buffer overflow
int safe_input(char *buffer, int size, const char *prompt) {
if (prompt) {
printf("%s", prompt);
}
if (fgets(buffer, size, stdin) == NULL) {
return 0; // EOF
}
// Remove trailing newline
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[len-1] = '\0';
} else if (len == size - 1) {
// Input was truncated, clear remaining input
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
}
return 1;
}
int main() {
char name[30];
char city[50];
char email[100];
safe_input(name, sizeof(name), "Enter name: ");
safe_input(city, sizeof(city), "Enter city: ");
safe_input(email, sizeof(email), "Enter email: ");
printf("\n--- User Info ---\n");
printf("Name: %s\n", name);
printf("City: %s\n", city);
printf("Email: %s\n", email);
return 0;
}
Formatted Input/Output Functions
1. sprintf() and sscanf()
#include <stdio.h>
int main() {
// sprintf: Write formatted data to string
char buffer[256];
int year = 2024;
float price = 99.99;
char product[] = "Widget";
int len = sprintf(buffer, "Product: %s, Year: %d, Price: $%.2f",
product, year, price);
printf("String: %s\n", buffer);
printf("Length: %d\n", len);
// sscanf: Read formatted data from string
char data[] = "John,25,85.5";
char name[30];
int age;
float score;
int items = sscanf(data, "%[^,],%d,%f", name, &age, &score);
printf("Parsed %d items: Name='%s', Age=%d, Score=%.1f\n",
items, name, age, score);
// Parse multiple formats from same string
char mixed[] = "ID: 12345 Price: $67.89";
int id;
float price2;
sscanf(mixed, "ID: %d Price: $%f", &id, &price2);
printf("ID: %d, Price: $%.2f\n", id, price2);
return 0;
}
2. fprintf() and fscanf()
#include <stdio.h>
int main() {
// fprintf to stderr (error output)
fprintf(stderr, "Error: Something went wrong\n");
// Can also write to files (covered in file I/O)
FILE *fp = fopen("output.txt", "w");
if (fp) {
fprintf(fp, "Writing to file\n");
fclose(fp);
}
// fscanf from string (like sscanf but with stream)
FILE *fp2 = fmemopen("42 3.14", 8, "r");
if (fp2) {
int num;
float fnum;
fscanf(fp2, "%d %f", &num, &fnum);
printf("Read: %d, %.2f\n", num, fnum);
fclose(fp2);
}
return 0;
}
Advanced Input Techniques
1. Reading Multiple Lines
#include <stdio.h>
#include <stdlib.h>
int main() {
// Read until EOF
printf("Enter text (Ctrl+D to end):\n");
char line[256];
int line_count = 0;
while (fgets(line, sizeof(line), stdin) != NULL) {
line_count++;
printf("%3d: %s", line_count, line);
}
printf("\nTotal lines: %d\n", line_count);
// Dynamic allocation for unknown line length
char *dynamic_line = NULL;
size_t len = 0;
ssize_t read;
printf("\nEnter text (Ctrl+D to end, dynamic):\n");
while ((read = getline(&dynamic_line, &len, stdin)) != -1) {
printf("Read %zd chars: %s", read, dynamic_line);
}
free(dynamic_line);
return 0;
}
2. Unformatted I/O
#include <stdio.h>
int main() {
// putchar() - character output
for (char c = 'A'; c <= 'Z'; c++) {
putchar(c);
}
putchar('\n');
// getchar() - character input
printf("Press any key: ");
int ch = getchar();
printf("You pressed: '%c' (ASCII %d)\n", ch, ch);
// Clear input buffer
int c;
while ((c = getchar()) != '\n' && c != EOF);
// Reading with getchar in a loop
printf("Enter numbers (end with .): ");
int total = 0;
while ((ch = getchar()) != '.' && ch != EOF) {
if (ch >= '0' && ch <= '9') {
total += ch - '0';
}
}
printf("Sum of digits: %d\n", total);
return 0;
}
Input Validation and Error Handling
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
// Validate integer input
int get_int(const char *prompt, int min, int max) {
int value;
char line[100];
int valid = 0;
do {
printf("%s", prompt);
if (fgets(line, sizeof(line), stdin) == NULL) {
return 0; // EOF
}
// Check if input is a valid integer
char *endptr;
long val = strtol(line, &endptr, 10);
if (endptr == line) {
printf("Invalid input: not a number\n");
} else if (*endptr != '\n' && *endptr != '\0') {
printf("Invalid input: extra characters after number\n");
} else if (val < min || val > max) {
printf("Invalid input: must be between %d and %d\n", min, max);
} else {
value = (int)val;
valid = 1;
}
} while (!valid);
return value;
}
// Validate float input
float get_float(const char *prompt, float min, float max) {
float value;
char line[100];
int valid = 0;
do {
printf("%s", prompt);
if (fgets(line, sizeof(line), stdin) == NULL) {
return 0.0f;
}
char *endptr;
float val = strtof(line, &endptr);
if (endptr == line) {
printf("Invalid input: not a number\n");
} else if (*endptr != '\n' && *endptr != '\0') {
printf("Invalid input: extra characters after number\n");
} else if (val < min || val > max) {
printf("Invalid input: must be between %.2f and %.2f\n", min, max);
} else {
value = val;
valid = 1;
}
} while (!valid);
return value;
}
// Validate yes/no input
int get_yes_no(const char *prompt) {
char response[10];
while (1) {
printf("%s (y/n): ", prompt);
if (fgets(response, sizeof(response), stdin) == NULL) {
return 0;
}
if (response[0] == 'y' || response[0] == 'Y') {
return 1;
} else if (response[0] == 'n' || response[0] == 'N') {
return 0;
}
printf("Please enter 'y' or 'n'\n");
}
}
// Main example
int main() {
int age = get_int("Enter your age (1-120): ", 1, 120);
float height = get_float("Enter your height (0.5-3.0 m): ", 0.5, 3.0);
int student = get_yes_no("Are you a student");
printf("\n--- Results ---\n");
printf("Age: %d\n", age);
printf("Height: %.2f m\n", height);
printf("Student: %s\n", student ? "Yes" : "No");
return 0;
}
Complete Example: Interactive Calculator
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
// Function prototypes
void print_menu();
double get_number(const char *prompt);
char get_operator();
double calculate(double a, double b, char op);
int get_continue();
int main() {
printf("=== Interactive Calculator ===\n");
printf("This program performs basic arithmetic operations.\n\n");
do {
print_menu();
double num1 = get_number("Enter first number: ");
char op = get_operator();
double num2 = get_number("Enter second number: ");
double result = calculate(num1, num2, op);
printf("\nResult: %.2f %c %.2f = %.2f\n", num1, op, num2, result);
} while (get_continue());
printf("\nThank you for using the calculator!\n");
return 0;
}
void print_menu() {
printf("\n--- Calculator Menu ---\n");
printf(" + Addition\n");
printf(" - Subtraction\n");
printf(" * Multiplication\n");
printf(" / Division\n");
printf(" %% Modulus (integers only)\n");
printf("------------------------\n");
}
double get_number(const char *prompt) {
double value;
char line[100];
int valid = 0;
do {
printf("%s", prompt);
if (fgets(line, sizeof(line), stdin) == NULL) {
exit(1);
}
char *endptr;
double val = strtod(line, &endptr);
if (endptr == line) {
printf("Invalid input. Please enter a number.\n");
} else if (*endptr != '\n' && *endptr != '\0') {
printf("Invalid input. Extra characters after number.\n");
} else {
value = val;
valid = 1;
}
} while (!valid);
return value;
}
char get_operator() {
char line[10];
char op;
int valid = 0;
do {
printf("Enter operator (+, -, *, /, %%): ");
if (fgets(line, sizeof(line), stdin) == NULL) {
exit(1);
}
op = line[0];
if (op == '+' || op == '-' || op == '*' || op == '/' || op == '%') {
valid = 1;
} else {
printf("Invalid operator. Please enter one of: + - * / %%\n");
}
} while (!valid);
return op;
}
double calculate(double a, double b, char op) {
switch (op) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0) {
printf("Error: Division by zero!\n");
return 0;
}
return a / b;
case '%':
{
int ia = (int)a;
int ib = (int)b;
if (ib == 0) {
printf("Error: Modulus by zero!\n");
return 0;
}
return ia % ib;
}
default:
return 0;
}
}
int get_continue() {
char line[10];
while (1) {
printf("\nContinue? (y/n): ");
if (fgets(line, sizeof(line), stdin) == NULL) {
return 0;
}
if (line[0] == 'y' || line[0] == 'Y') {
return 1;
} else if (line[0] == 'n' || line[0] == 'N') {
return 0;
}
printf("Please enter 'y' or 'n'\n");
}
}
Buffer Management and Common Pitfalls
#include <stdio.h>
// Common problem: leftover newline in buffer
void buffer_problem_demo() {
int num;
char str[50];
printf("Enter a number: ");
scanf("%d", &num);
// Problem: newline remains in buffer
printf("Enter a string: ");
fgets(str, sizeof(str), stdin); // Reads empty line!
printf("Number: %d, String: '%s'\n", num, str);
}
// Solution: clear buffer
void clear_input_buffer() {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
// Correct implementation
void correct_input() {
int num;
char str[50];
printf("Enter a number: ");
scanf("%d", &num);
// Clear the buffer
clear_input_buffer();
printf("Enter a string: ");
fgets(str, sizeof(str), stdin);
// Remove trailing newline
size_t len = strlen(str);
if (len > 0 && str[len-1] == '\n') {
str[len-1] = '\0';
}
printf("Number: %d, String: '%s'\n", num, str);
}
Summary Table of I/O Functions
| Function | Purpose | Input/Output | Formatting |
|---|---|---|---|
printf() | Formatted output | stdout | Yes |
scanf() | Formatted input | stdin | Yes |
putchar() | Single character | stdout | No |
getchar() | Single character | stdin | No |
puts() | String with newline | stdout | No |
fgets() | Safe string input | stdin | No |
sprintf() | Formatted to string | String | Yes |
sscanf() | Parse from string | String | Yes |
fprintf() | Formatted to file | File | Yes |
fscanf() | Formatted from file | File | Yes |
Best Practices Summary
- Always check return values: Verify I/O operations succeeded
- Use fgets() instead of gets(): Never use gets() - it's dangerous
- Limit input size: Prevent buffer overflows
- Clear input buffer: Handle leftover newlines
- Validate user input: Check ranges and formats
- Use appropriate format specifiers: Match variable types
- Handle EOF properly: Check for end-of-file conditions
- Flush output when needed: Use
fflush()for interactive programs - Use stderr for errors: Keep error messages separate
- Consider line-buffering: Understand buffering behavior
Conclusion
Mastering basic I/O in C is essential for creating interactive programs. The printf and scanf families provide powerful formatting capabilities, while functions like fgets and getchar offer more control over input handling. Understanding buffer management, input validation, and proper error handling will help you create robust applications that handle any input gracefully.
The examples and patterns in this guide provide a solid foundation for all your C programming I/O needs. Remember that good I/O handling is not just about reading and writing data—it's about creating programs that are user-friendly, secure, and resilient to unexpected input.