C Programming: Low-Level Mastery
HomeInsightsCoursesC ProgrammingBasic Input/Output (printf, scanf)
C Fundamentals

Input & Output Functions

Master C's standard I/O functions for reading user input and displaying output. Learn printf format specifiers, scanf for input, and handle common I/O pitfalls safely.

Understanding Standard I/O

Input/Output (I/O) operations allow programs to communicate with users and external systems. C provides standard I/O functions through the stdio.h header library. These functions handle console input (keyboard), console output (screen), and file operations. The standard I/O library abstracts platform differences, providing consistent behavior across operating systems.

Three standard streams are automatically opened when a C program starts: stdin (standard input, typically keyboard), stdout (standard output, typically screen), and stderr (standard error, for error messages). Functions like printf() write to stdout, scanf() reads from stdin, and fprintf() can write to any stream including stderr for error messages.

Common I/O Functions:

  • printf(): Formatted output to screen
  • scanf(): Formatted input from keyboard
  • getchar(): Read single character
  • putchar(): Write single character
  • gets()/fgets(): Read string (fgets preferred)
  • puts(): Write string with newline

printf() - Formatted Output

printf() (print formatted) is the most commonly used output function in C. It takes a format string containing text and format specifiers, followed by variables to print. Format specifiers begin with % and tell printf() how to display each variable. Understanding format specifiers is essential for controlling output appearance.

C
#include <stdio.h>

int main(void) {
    /* Basic printf usage */
    printf("Hello, World!\n");
    
    /* Integer format specifiers */
    int age = 25;
    printf("Age: %d\n", age);          // %d for signed decimal int
    printf("Age: %i\n", age);          // %i same as %d
    
    unsigned int count = 100;
    printf("Count: %u\n", count);      // %u for unsigned int
    
    /* Hexadecimal and octal */
    int value = 255;
    printf("Hex: %x\n", value);        // ff (lowercase)
    printf("Hex: %X\n", value);        // FF (uppercase)
    printf("Hex: 0x%x\n", value);      // 0xff (with prefix)
    printf("Octal: %o\n", value);      // 377
    
    /* Long and long long */
    long big = 1000000L;
    printf("Long: %ld\n", big);        // %ld for long
    
    long long huge = 9223372036854775807LL;
    printf("Long long: %lld\n", huge); // %lld for long long
    
    return 0;
}

Floating-Point Format Specifiers

C
/* Float and double */
float pi = 3.14159f;
double e = 2.71828;

printf("Float: %f\n", pi);            // 3.141590 (default 6 decimals)
printf("Double: %f\n", e);            // 2.718280
printf("Double: %lf\n", e);           // %lf also works for double

/* Control decimal places */
printf("Pi: %.2f\n", pi);             // 3.14 (2 decimals)
printf("Pi: %.4f\n", pi);             // 3.1416 (4 decimals)

/* Scientific notation */
double large = 1234567.89;
printf("Scientific: %e\n", large);    // 1.234568e+06 (lowercase)
printf("Scientific: %E\n", large);    // 1.234568E+06 (uppercase)

/* Shortest representation */
printf("g format: %g\n", large);      // 1.23457e+06 or 1234568
printf("G format: %G\n", large);      // Uppercase E if scientific

/* Width and precision */
printf("%10.2f\n", pi);               // "      3.14" (width 10, 2 decimals)

Character and String Format Specifiers

C
/* Characters */
char letter = 'A';
printf("Char: %c\n", letter);         // A
printf("ASCII: %d\n", letter);        // 65 (using %d shows numeric value)

/* Strings */
char *name = "Alice";
printf("Name: %s\n", name);           // Alice

/* String width and alignment */
printf("%10s\n", "Hi");               // "        Hi" (right-aligned)
printf("%-10s\n", "Hi");              // "Hi        " (left-aligned)
printf("%.5s\n", "HelloWorld");       // "Hello" (max 5 chars)

/* Multiple arguments */
char *first = "John";
char *last = "Doe";
int age = 30;
printf("%s %s is %d years old\n", first, last, age);
// Output: John Doe is 30 years old

Advanced printf() Features

C
/* Field width and alignment */
int num = 42;
printf("%5d\n", num);                 // "   42" (width 5, right-aligned)
printf("%-5d\n", num);                // "42   " (width 5, left-aligned)
printf("%05d\n", num);                // "00042" (zero-padded)

/* Pointers */
int x = 10;
int *ptr = &x;
printf("Address: %p\n", (void*)ptr);  // 0x7ffd12345678 (hex address)

/* Percent sign */
printf("100%% complete\n");           // 100% complete (escape %)

/* Size_t for sizeof */
printf("Size: %zu bytes\n", sizeof(int));  // %zu for size_t

/* Return value of printf */
int chars_printed = printf("Hello\n");
printf("Printed %d characters\n", chars_printed);  // Printed 6 characters

scanf() - Formatted Input

scanf() reads formatted input from the keyboard (stdin). It uses the same format specifiers as printf() but works in reverse - parsing input text and storing values into variables. A critical requirement: you must pass addresses of variables using the & operator (except for strings/arrays which are already addresses).

scanf() is powerful but has pitfalls. It leaves newline characters in the input buffer, can overflow buffers if not careful, and doesn't provide good error handling. Despite these issues, it remains widely used for simple input. Understanding its behavior helps avoid common mistakes.

C
#include <stdio.h>

int main(void) {
    /* Basic scanf usage */
    int age;
    printf("Enter your age: ");
    scanf("%d", &age);  // MUST use & for non-array variables
    printf("You are %d years old\n", age);
    
    /* Multiple inputs */
    int day, month, year;
    printf("Enter date (DD MM YYYY): ");
    scanf("%d %d %d", &day, &month, &year);
    printf("Date: %d/%d/%d\n", day, month, year);
    
    /* Float input */
    float height;
    printf("Enter height in meters: ");
    scanf("%f", &height);  // %f for float, %lf for double
    printf("Height: %.2f meters\n", height);
    
    /* Double input */
    double price;
    printf("Enter price: ");
    scanf("%lf", &price);  // %lf for double (note: different from printf)
    
    /* Character input */
    char grade;
    printf("Enter grade: ");
    scanf(" %c", &grade);  // Space before %c skips whitespace
    printf("Grade: %c\n", grade);
    
    return 0;
}

scanf() Common Pitfalls

C
/* Problem 1: Newline left in buffer */
int num;
char letter;

printf("Enter number: ");
scanf("%d", &num);              // Reads number, leaves \n in buffer

printf("Enter letter: ");
scanf("%c", &letter);           // Immediately reads leftover \n!

/* Solution: Space before %c to skip whitespace */
scanf(" %c", &letter);          // Notice space before %c

/* Problem 2: String input buffer overflow */
char name[10];
printf("Enter name: ");
scanf("%s", name);              // DANGEROUS: No length limit!
// If user enters 50 characters, buffer overflow!

/* Solution: Limit input length */
scanf("%9s", name);             // Read max 9 chars (+ \0)

/* Better solution: Use fgets instead */
fgets(name, sizeof(name), stdin);

/* Problem 3: Return value ignored */
int value;
scanf("%d", &value);            // What if user enters letters?

/* Solution: Check return value */
if (scanf("%d", &value) == 1) {
    printf("Got: %d\n", value);
} else {
    printf("Invalid input\n");
    // Clear input buffer
    while (getchar() != '\n');
}

/* Problem 4: Mixed numeric and string input */
int age;
char name[50];

scanf("%d", &age);              // Reads age, leaves \n
scanf("%s", name);              // Skips whitespace including \n - OK

/* But with fgets after scanf: */
scanf("%d", &age);
fgets(name, 50, stdin);         // Immediately reads leftover \n!

/* Solution: Clear buffer after scanf */
scanf("%d", &age);
while (getchar() != '\n');     // Consume leftover newline
fgets(name, 50, stdin);         // Now works correctly

Character I/O Functions

For single character input/output, C provides simpler functions than scanf/printf. These functions are faster and easier to use when you only need one character at a time. They're commonly used in loops to process text character by character.

C
/* getchar() - read single character */
int ch;  // Use int, not char, to detect EOF
printf("Enter a character: ");
ch = getchar();
printf("You entered: %c\n", ch);

/* putchar() - write single character */
putchar('H');
putchar('i');
putchar('\n');  // Output: Hi

/* Reading until newline */
printf("Type something: ");
while ((ch = getchar()) != '\n') {
    putchar(ch);  // Echo each character
}
putchar('\n');

/* Reading entire line */
char line[100];
int i = 0;
printf("Enter line: ");
while ((ch = getchar()) != '\n' && i < 99) {
    line[i++] = ch;
}
line[i] = '\0';  // Null terminate
printf("You entered: %s\n", line);

/* Character input in loop */
printf("Enter characters (q to quit):\n");
while ((ch = getchar()) != 'q') {
    if (ch != '\n') {  // Skip printing newlines
        printf("Got: %c\n", ch);
    }
}

String I/O Functions

For string input, fgets() is much safer than gets() or scanf(). The gets() function was deprecated and removed from C11 because it causes buffer overflows - it has no way to limit input length. Always use fgets() for string input. For output, puts() is simpler than printf() when you just need to print a string with a newline.

C
#include <stdio.h>
#include <string.h>

int main(void) {
    char name[50];
    
    /* NEVER use gets() - it's dangerous! */
    // gets(name);  // Removed in C11, causes buffer overflows
    
    /* Use fgets() instead */
    printf("Enter your name: ");
    fgets(name, sizeof(name), stdin);
    
    /* fgets includes newline - remove it */
    name[strcspn(name, "\n")] = '\0';
    // Or: size_t len = strlen(name);
    //     if (len &gt; 0 && name[len-1] == '\n')
    //         name[len-1] = '\0';
    
    printf("Hello, %s!\n", name);
    
    /* puts() - writes string with newline */
    puts("This is a line");
    puts("This is another line");
    // Equivalent to:
    // printf("This is a line\n");
    // printf("This is another line\n");
    
    /* fputs() - write string without newline */
    fputs("No newline", stdout);
    fputs(" added\n", stdout);
    // Output: No newline added
    
    /* Reading multiple lines */
    char lines[3][100];
    printf("Enter 3 lines:\n");
    for (int i = 0; i < 3; i++) {
        printf("Line %d: ", i + 1);
        fgets(lines[i], 100, stdin);
        lines[i][strcspn(lines[i], "\n")] = '\0';  // Remove \n
    }
    
    printf("\nYou entered:\n");
    for (int i = 0; i < 3; i++) {
        printf("%d: %s\n", i + 1, lines[i]);
    }
    
    return 0;
}

Best Practices for I/O

Safe input handling requires defensive programming. Always validate input, limit string lengths, and handle errors gracefully. These practices prevent crashes, security vulnerabilities, and frustrating user experiences.

C
/* Safe integer input with validation */
int get_integer(const char *prompt) {
    int value;
    int result;
    
    while (1) {
        printf("%s", prompt);
        result = scanf("%d", &value);
        
        if (result == 1) {
            /* Clear rest of line */
            while (getchar() != '\n');
            return value;
        }
        
        /* Invalid input */
        printf("Invalid input. Please enter a number.\n");
        /* Clear invalid input */
        while (getchar() != '\n');
    }
}

/* Safe string input */
void get_string(char *buffer, size_t size, const char *prompt) {
    printf("%s", prompt);
    
    if (fgets(buffer, size, stdin) != NULL) {
        /* Remove newline if present */
        buffer[strcspn(buffer, "\n")] = '\0';
    }
}

/* Usage */
int main(void) {
    int age = get_integer("Enter your age: ");
    printf("Age: %d\n", age);
    
    char name[50];
    get_string(name, sizeof(name), "Enter your name: ");
    printf("Name: %s\n", name);
    
    /* Check scanf return value */
    int x, y;
    printf("Enter two numbers: ");
    if (scanf("%d %d", &x, &y) == 2) {
        printf("Sum: %d\n", x + y);
    } else {
        printf("Error: Expected two integers\n");
        while (getchar() != '\n');  // Clear buffer
    }
    
    return 0;
}

Summary & What's Next

Key Takeaways:

  • ✅ printf() formats and displays output
  • ✅ Format specifiers: %d (int), %f (float), %c (char), %s (string)
  • ✅ scanf() reads formatted input - MUST use & for addresses
  • ✅ Use fgets() instead of gets() for safe string input
  • ✅ getchar()/putchar() for single character I/O
  • ✅ Always check scanf() return value for errors
  • ✅ Clear input buffer after scanf() before other input
  • ✅ Limit string input length to prevent buffer overflow

What's Next?

Now let's learn about operators in C!