CStatements & Semicolons

Statements & Semicolons

A C program is built out of statements — individual instructions that tell the computer to do something: compute a value, call a function, make a decision, repeat a block of code. Almost every statement in C ends with a semicolon ;. The semicolon isn't decoration — it's how the compiler knows where one statement ends and the next begins.

The classic "missing semicolon" error

Forgetting a semicolon is probably the single most common mistake every C beginner makes, and the compiler's error message can be confusing at first because it usually points at the next line, not the one that's actually missing the semicolon:

C
#include <stdio.h>

int main(void) {
    int total = 10   // <-- missing semicolon here
    printf("%d\n", total);
    return 0;
}
Reading the compiler's error correctly
A compiler like GCC will typically report something like `error: expected ';' before 'printf'` at the line containing `printf`, not the line with `int total = 10`. That's because the compiler only realizes something is wrong once it starts parsing the *next* statement and finds a token it didn't expect. When you see this kind of error, the fix is almost always to look at the line **above** the one reported.
What counts as a statement

Most lines of C code you write are one of these kinds of statements:

  • Expression statements — an expression followed by a semicolon, like total = total + 1; or printf("hi");. The expression is evaluated and its result (if any) is discarded.

  • Declaration statements — introduce a new variable, like int count = 0;.

  • Control-flow statementsif, for, while, switch, return, break, continue, goto.

  • Compound statements (blocks) — a sequence of statements wrapped in { }, which itself counts as a single statement wherever a statement is expected.

The empty statement

A lone semicolon ; is itself a valid statement — the empty statement. It does nothing. This is occasionally useful, most often as the body of a loop that does all of its work in the loop header itself:

C
/* Skip characters until we hit a newline or the end of the string. */
while (*p != '\n' && *p != '\0') {
    p++;
}

/* The same idea written with an empty statement as the loop body. */
while (*p != '\n' && *p != '\0')
    ;
p++; /* still needs to advance past the character that stopped the loop */
A stray semicolon can silently change your program
A `;` placed right after an `if (...)` or a `for (...)` header creates an empty statement as the body, and the "real" body below it runs unconditionally. This compiles cleanly and is a very common source of subtle bugs:

C
if (count > MAX_ALLOWED); /* <-- oops, empty statement! */
{
    printf("Too many items!\n"); /* this block always runs */
}
Compound statements and blocks
Curly braces `` group zero or more statements into a single **compound statement**, also called a **block**. Blocks are what let you attach multiple statements to a single `if`, `for`, `while`, or function body. A block does **not** end with a semicolon after its closing brace (function definitions and control-flow bodies never need one) — the exception is a `struct`, `union`, or `enum` declaration, which does require a trailing semicolon.

C
if (score >= 90) {
    grade = 'A';
    printf("Excellent!\n");
} /* no semicolon needed here */

struct Point {
    int x;
    int y;
}; /* semicolon required here — this is a declaration */
Expression statements vs declaration statements
An **expression statement** evaluates something and throws away the value if it isn't used, e.g. `x + 1;` is legal C — it computes `x + 1` and does nothing with it, which is why compilers warn about "statement with no effect" for code like that. A **declaration statement** instead introduces a new name into scope, like `double rate;`. Both end in a semicolon, but they serve very different purposes.