Most C tutorials introduce scanf for input and printf for output. Both are powerful but heavyweight. For character-by-character processing — copying input, counting bytes, filtering text — getchar and putchar are simpler, faster, and avoid the input buffer headaches that make scanf frustrating.

The Basics

#include <stdio.h>

int main() {
    int c = getchar();  /* read one character from stdin */

    if (c == EOF) {
        printf("End of inputn");
    } else {
        putchar(c);     /* write one character to stdout */
        putchar('n');
    }

    return 0;
}

getchar reads the next character from standard input and returns it as an int, not a char. putchar writes a character to standard output. Both are typically implemented as macros for efficiency — no function call overhead.

Why getchar Returns int, Not char

#include <stdio.h>
#include <stdint.h>

int main() {
    /* WRONG: stores result in char */
    char c = getchar();
    if (c == EOF) {  /* BUG on systems where char is unsigned */
        printf("End of inputn");
    }

    /* CORRECT: int can hold all char values AND EOF */
    int ci = getchar();
    if (ci == EOF) {
        printf("End of inputn");
    }

    return 0;
}

EOF is typically -1. On systems where char is unsigned, -1 cannot be stored in a char — it wraps to 255. Then c == EOF is comparing 255 to -1, which is false. The program never detects end-of-input and loops forever. Always use int to store the result of getchar.

Copying Input — The Classic Pattern

#include <stdio.h>

int main() {
    int c;
    while ((c = getchar()) != EOF) {
        putchar(c);
    }
    return 0;
}

This reads and copies stdin to stdout until end-of-file. Run it, type text, and press Ctrl+D (Unix) or Ctrl+Z Enter (Windows) to send EOF. This is the building block for text processing tools — filter a line, transform characters, count occurrences.

Counting Lines, Words, and Characters

#include <stdio.h>

int main() {
    int c;
    long chars = 0, words = 0, lines = 0;
    int in_word = 0;

    while ((c = getchar()) != EOF) {
        chars++;

        if (c == 'n') lines++;

        if (c == ' ' || c == 't' || c == 'n') {
            in_word = 0;
        } else if (!in_word) {
            in_word = 1;
            words++;
        }
    }

    printf("%8ld %8ld %8ldn", lines, words, chars);
    return 0;
}

This is a simplified wc. Same logic, same output format. The in_word flag tracks whether the previous character was part of a word — a clean two-state approach that avoids look-ahead.

Using getchar to Clear the Input Buffer

#include <stdio.h>

int main() {
    int n;
    printf("Enter a number: ");
    scanf("%d", &n);

    /* Clear the rest of the line (including the newline scanf left) */
    int c;
    while ((c = getchar()) != 'n' && c != EOF);

    printf("Enter a character: ");
    char ch = (char)getchar();   /* now reads the character, not the leftover newline */
    printf("Got: %cn", ch);

    return 0;
}

This is the correct fix for the scanf newline problem: after reading a number with scanf, use a getchar loop to consume everything up to and including the newline. It is more reliable than adding a space in the format string and clearer in intent.

ungetc — Pushing a Character Back

#include <stdio.h>

int main() {
    int c;

    /* Peek at the next character without consuming it */
    c = getchar();
    if (c == '-') {
        /* negative number — consume the sign and read digits */
    } else {
        ungetc(c, stdin);  /* push it back — next getchar reads it again */
    }

    /* Now read whatever follows */
    scanf("%d", &c);
    printf("Value: %dn", c);

    return 0;
}

ungetc pushes one character back into the stream’s internal buffer. The next getchar (or any read) will see it again. POSIX guarantees at least one byte of pushback. This is useful for look-ahead parsing where you need to check the next character before deciding how to process it.

getchar vs fgetc

int c1 = getchar();            /* reads from stdin */
int c2 = fgetc(stdin);         /* equivalent — reads from a FILE* */
int c3 = fgetc(fp);            /* reads from any open FILE* */

getchar() is defined as fgetc(stdin). Use fgetc when you need to read from a file. The return type, EOF handling, and int-not-char requirement are identical. Test character I/O in our run c online compiler — type input in the input box and see getchar processing it character by character.

TL;DR

  • Always store getchar() in an int, never a char — you need int to distinguish EOF from valid characters
  • while ((c = getchar()) != EOF) is the canonical input loop
  • Use getchar to clear the stdin buffer after scanf — more explicit than format-string tricks
  • ungetc(c, stdin) pushes one character back for look-ahead parsing
  • getchar() == fgetc(stdin) — use fgetc when reading from files
  • Both are typically macros — no function call overhead in most implementations
  • EOF is typically -1; char may be unsigned on some platforms — use int to avoid the comparison bug