scanf is the first input function most C programmers learn and one of the last they fully understand. Its failure modes are silent — it returns without reading anything and leaves garbage in your variables. Here is what is actually happening.

How scanf Parses Input

#include <stdio.h>

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

    if (result == 1) {
        printf("You entered: %dn", n);
    } else {
        printf("scanf failed — read %d itemsn", result);
    }

    return 0;
}

scanf returns the number of items successfully read and assigned. Always check this return value. If the user types “abc” instead of a number, scanf returns 0, leaves n uninitialized, and the “abc” stays in the input buffer.

The Input Buffer Problem

#include <stdio.h>

int main() {
    int a, b;

    printf("Enter two numbers: ");
    scanf("%d", &a);
    scanf("%d", &b);

    printf("Sum: %dn", a + b);
    return 0;
}

If the user types 5 abc, the first scanf reads 5 successfully. The second scanf tries to read an integer but finds “abc” in the buffer — returns 0, leaves b uninitialized.

The problem: “abc” is still sitting in the input buffer. Every subsequent scanf call will hit it immediately and fail the same way. To clear the buffer:

/* Clear everything up to and including the newline */
int c;
while ((c = getchar()) != 'n' && c != EOF);

The Newline Left in the Buffer

This is the most common scanf gotcha:

#include <stdio.h>

int main() {
    int n;
    char ch;

    scanf("%d", &n);
    scanf("%c", &ch);   /* reads the 'n' left by the first scanf */

    printf("n=%d ch=%dn", n, (int)ch);   /* ch is 'n' (10), not what user typed */
    return 0;
}

After reading an integer, the newline the user pressed is still in the buffer. The next scanf("%c") immediately reads that newline instead of waiting for new input.

Fix: add a space before %c to skip whitespace:

scanf(" %c", &ch);   /* the space skips any pending whitespace including n */

Buffer Overflow with %s

#include <stdio.h>

int main() {
    char name[10];
    scanf("%s", name);   /* dangerous — no length limit */
    /* User types 20 characters: buffer overflow */
    printf("Hello, %sn", name);
    return 0;
}

scanf("%s") has no bounds checking. If the user types more characters than the buffer holds, it writes past the array boundary — a classic stack buffer overflow. Always specify a width:

scanf("%9s", name);   /* reads at most 9 characters, leaves room for  */

Why & Is Required (And When It Is Not)

scanf needs the address of the variable to write into. This is why you must pass &variable, not variable. If you forget the &, you are passing the value of the variable as an address — undefined behaviour and almost certainly a crash. See our guide on how pointers work in C for why this address-passing is necessary.

int n;
scanf("%d", n);    /* WRONG — passing value, not address */
scanf("%d", &n);  /* CORRECT */

The exception: string arrays. The array name already decays to a pointer:

char name[50];
scanf("%49s", name);   /* no & needed — name is already an address */

Safer Alternative: fgets + sscanf

#include <stdio.h>
#include <stdlib.h>

int main() {
    char line[256];
    int n;

    printf("Enter a number: ");
    if (fgets(line, sizeof(line), stdin) == NULL) return 1;

    if (sscanf(line, "%d", &n) != 1) {
        fprintf(stderr, "Invalid inputn");
        return 1;
    }

    printf("You entered: %dn", n);
    return 0;
}

fgets reads an entire line into a buffer (including the newline), consuming it completely. Then sscanf parses the buffer string. If parsing fails, the buffer is already consumed — there is nothing stuck in stdin to cause problems on the next read.

scanf Format Specifiers Reference

Specifier Reads Argument type
%d Decimal integer int*
%f Float float*
%lf Double double*
%c Single character char*
%s Whitespace-delimited string char*
%[^n] Everything up to newline char*

TL;DR

  • Always check scanf’s return value — it returns the number of items read
  • Failed reads leave the bad input in the buffer — clear it before retrying
  • Add a space before %c to skip the leftover newline: " %c"
  • Always specify a width with %s: "%49s" for a 50-byte buffer
  • Pass addresses with & — except for string arrays
  • Prefer fgets + sscanf over raw scanf for robust input handling
  • You can practice all of these patterns in our run c online compiler — no local setup needed