Control flow, functions and the standard library

Branch and loop without off-by-one bugs, declare functions before use, and use printf and scanf without the classic traps.

Branching and looping

#include <stdio.h>

int classify(int n) {
    if (n < 0)        return -1;
    else if (n == 0)  return 0;
    else              return 1;
}

int main(void) {
    for (int i = 0; i < 5; i++) {           /* counted loop */
        printf("%d -> %d\n", i, classify(i));
    }

    int n = 10;
    while (n > 0) { n /= 2; }                /* condition first */

    do { n++; } while (n < 3);               /* body at least once */

    for (int i = 0; i < 6; i++) {
        if (i == 1) continue;                /* skip this iteration */
        if (i == 4) break;                   /* leave the loop */
        printf("keep %d\n", i);
    }

    switch (n) {
        case 1:  puts("one");   break;       /* break or you fall through */
        case 2:  puts("two");   break;
        default: puts("other"); break;
    }
    return 0;
}
  • switch only works on integer types. Omitting break falls through intentionally or accidentally, and the compiler will not warn without -Wimplicit-fallthrough.
  • There is no foreach. Arrays are iterated with an index or a pointer, and the length must be tracked separately.
  • for (int i = 0; i <= n; i++) over an array of size n is the single most common off-by-one bug in C.

Declare a function before you call it. A call to an undeclared function is an implicit declaration in older standards and an error in C23, and either way the compiler assumes int arguments, which silently corrupts pointer arguments.

printf, scanf and the headers you use

SpecifierTypeExample
%dintprintf("%d", 42)
%zusize_tprintf("%zu", sizeof(int))
%ld / %lldlong / long longprintf("%lld", (long long)n)
%f / %.2fdoubleprintf("%.2f", 3.14159)
%cchar after promotionprintf("%c", 'A')
%schar *printf("%s", name)
%pany pointer, cast to void *printf("%p", (void *)ptr)
%xunsigned int in hexprintf("%x", 255)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

int main(void) {
    char name[64];
    int age = 0;

    printf("name and age: ");
    /* limit the width so a long line cannot overflow name */
    if (scanf("%63s %d", name, &age) != 2) {
        fprintf(stderr, "bad input\n");
        return EXIT_FAILURE;
    }

    /* build into a fixed buffer with a hard bound */
    char line[128];
    int written = snprintf(line, sizeof line, "%s is %d", name, age);
    if (written < 0 || (size_t)written >= sizeof line) {
        fprintf(stderr, "truncated\n");
    }
    puts(line);
    return EXIT_SUCCESS;
}
  • <stdio.h> printf/scanf, <stdlib.h> malloc/exit/strtol, <string.h> strlen/memcpy, <stdint.h> fixed-width integers, <stdbool.h> bool, <ctype.h> isdigit/toupper, <errno.h> error codes, <time.h> time and clock, <limits.h> INT_MAX and friends, <math.h> maths (link with -lm).
  • Passing %d a long is undefined behaviour: printf reads four bytes where eight were pushed. -Wformat (part of -Wall) catches this at compile time.
  • scanf leaves unread characters in the buffer when it fails, so a retry loop can spin forever on the same bad input.
⚠️
Never pass user-controlled text as the format string: printf(user_input) is a format-string vulnerability. Always write printf("%s", user_input).

FAQ

Why does scanf stop reading?
It returned fewer conversions than you asked for. Check the return value, and consume the rest of the line (for example with fgets or %*[^\n]) before reading again.
Should I use gets or strcpy?
No. gets was removed from the standard in C11 and strcpy has no bound. Use fgets, snprintf, or memcpy with a checked length.

Syntax and the compiled toolchain Arrays, strings and the string library

Last refreshed 2026-09-18.