Anatomy of a C file
C is a compiled, procedural programming language known for its efficiency and low-level memory access.
File extensions: .c, .h
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a C file
Comment
Block (/* ... */) or single-line (//), ignored by the compiler.
A /* ... */ block comment can span multiple lines but does not nest, so an errant */ earlier in the file silently ends the comment early. // single-line comments run to the end of the line; they were a compiler extension in older C but became standard with C99. Comments are stripped by the compiler before compilation proper begins and have zero effect on the compiled binary. Because C gives no runtime introspection like a docstring, comments are the only place to record intent: header files in particular lean on them to document the API a .c file exposes.
Preprocessor directive
Processed before compilation; starts with #.
Every line beginning with # is handled by the preprocessor, a text-substitution pass that runs before the compiler ever sees C syntax. #include <stdio.h> copy-pastes the named header's declarations into the file: angle brackets search the system include path, while quotes ("myheader.h") search the local directory first. #define MAX_COUNT 10 declares a macro: every later occurrence of MAX_COUNT is textually replaced with 10 before compilation. Because macros are pure text substitution, they have no type and no scope. #define SQUARE(x) x*x will misbehave on SQUARE(1+2) unless parenthesized as ((
Variable declaration
Declares a variable with an explicit type.
C is statically typed: int global_var = 0; fixes both the name and the type (int) at compile time, and that type never changes for the life of the variable. A declaration outside any function, like global_var here, has file scope and lives for the whole run of the program; one declared inside a function, like int i;, is local and only exists while that function is on the stack. Unlike some languages, C does not require an initializer. int i; leaves i holding indeterminate garbage until something assigns it a value, which is a common source of bugs. Global variables default to zero-initialized
Function definition
A prototype declares a function's signature; the definition supplies its body.
void print_message(char *msg); is a prototype. It tells the compiler a function's name, parameter types, and return type so calls earlier in the file can be checked before the real body appears (often further down, or in another .c file after being declared in a shared header). The matching definition, void print_message(char *msg) { ... }, supplies the actual implementation. A void return type means the function gives nothing back to its caller. Parameters are passed by value in C: char *msg passes a copy of the pointer, not the string itself, which is how a function can read or modify the ca
String literal
A sequence of characters enclosed in double quotes.
C has no dedicated string type; "Hello, C World!" is really an array of char terminated by an invisible null byte (\0), and a char * variable just points at its first character. The compiler places string literals in read-only memory, so writing through a pointer to one is undefined behavior even though the pointer itself is not declared const. Escape sequences like \n (newline) and \" (a literal quote) are interpreted inside the quotes. Because the terminator is implicit, functions that operate on strings (strlen, strcpy, printf's %s) all scan forward until they hit that null byte, so a missi
Function call
Executes a function by using its name followed by ().
printf("Starting program...\n"); calls the standard library function printf, passing it a format string; print_message(greeting) calls a function defined in this same file, passing a pointer along. In both cases the arguments are evaluated, copied onto the call, and control jumps into the function body until it returns. printf is variadic. It accepts a variable number of arguments after the format string, matching each %s, %d, or %.2f conversion in order. The compiler does not verify at compile time that the arguments match the format specifiers unless it has special-cased printf (as GCC and C
Control flow (loop)
Repeats code (for, while, do...while).
for (i = 0; i < MAX_COUNT; i++) { ... } packs initialization, a continuation test, and a per-iteration update into one header, then repeats the body while the middle clause stays true. while (cond) { ... } checks before each pass and may run zero times; do { ... } while (cond); checks after, guaranteeing at least one execution. break exits the nearest enclosing loop immediately, and continue skips straight to the next iteration's update/test. Because C has no built-in iteration over collections, loops almost always drive a raw index or pointer by hand, which is faster but pushes bounds-checkin
Control flow (conditional)
Executes code based on a condition (if, else, switch).
if (i % 2 == 0) { ... } runs its block only when the parenthesized expression evaluates to nonzero. C has no dedicated boolean type before C99's _Bool/stdbool.h, so any nonzero integer counts as true and 0 counts as false. else and else if chain additional branches, and switch dispatches on an integer or enum value across several case labels. A classic pitfall is switch fallthrough: execution continues into the next case unless a break stops it, which is sometimes intentional but often a bug. % here is the modulo operator, so i % 2 == 0 is the idiomatic C test for an even number.
Return statement
Exits the function, optionally returning a value.
return 0; inside main both ends the function and hands 0 back to the operating system as the process's exit status, where 0 conventionally means success and any nonzero value signals an error. A function declared with a non-void return type must return a value of that type on every path; a void function may use a bare return; to exit early or simply fall off the closing brace. Reaching the end of main without an explicit return is one special case C allows to implicitly return 0, but relying on that is considered poor style. Every other function must return explicitly if its type demands a val
Official C site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.