Why break a program into pieces at all
So far your programs have been one long story: declare a variable, make a decision with control flow, loop a little, print a result. That works for ten lines. It falls apart at two hundred, because you start copying the same five lines of work to three different places, and when one copy has a bug, the other two keep it. A function is the cure: a named, reusable chunk of work you write once and call by name wherever you need it.
Think of a function as a tiny machine with a slot for input and a chute for output. You hand it some values, it does its job behind a closed door, and it hands you back a single result. You do not need to know how it works inside to use it — you only need its name and what it expects. You have already been using one all along: print is a function, and so is the very entry point of your program, main().
Anatomy of a function definition
A function definition has four parts, and once you can name them you can read any function in any C file. First the return type (what comes back), then the name, then the parameter list in parentheses (what goes in), then the body in braces (the work). Here is a function that returns the larger of two integers.
Reading left to right, that header is `int max_of(int a, int b)`: the return type `int`, the name `max_of`, and two parameters `int a` and `int b`. The body in braces does the work — `if (a > b) return a;` then `return b;` — and a call site writes `int biggest = max_of(7, 3);`, after which `biggest` holds 7. The words `a` and `b` inside the parentheses are parameters: fresh local variables that exist only while the function runs. The values 7 and 3 you write at the call site are the arguments.
The keyword return both hands back a value and ends the function immediately — any code written after a return that runs is skipped. A function whose return type is `void` hands nothing back; it is called purely for its effect, like printing. Once you can spot those four parts — return type, name, parameters, body — you can read any function in any C file.
Arguments are copied in: pass by value
Here is the single most important rule about C functions, and the one beginners trip over most: C is pass by value. When you call max_of(7, 3), the function does not get your original variables — it gets fresh copies of their values. Whatever the function does to its parameters changes only its own private copies; your variables back at the call site are untouched.
Make this concrete. Suppose `void try_to_change(int n)` does just one thing, `n = 999;`, and you call it as `int x = 5; try_to_change(x);`. After the call, `x` is still 5 — the assignment landed on the function's private copy, and that copy was thrown away when the function returned. Assigning to a parameter changes the copy, never the caller's variable.
Prototypes: telling the compiler before you tell yourself
The C compiler reads your file top to bottom, once. If main() calls max_of before the compiler has seen max_of's definition, it does not know what arguments max_of expects or what it returns. The fix is a function prototype: a one-line declaration of the function's shape, written before any call, with the body replaced by a semicolon.
Concretely, you write `int max_of(int a, int b);` near the top — same header as the definition, but ending in a semicolon instead of a body. Now `main` can call `max_of(7, 3)` even though the full definition `int max_of(int a, int b) { return a > b ? a : b; }` appears further down the file: the prototype already told the compiler the call is well-formed. The prototype lets the call come before the definition.
Prototypes are how big programs split work across files: one file lists the prototypes (a header), other files provide the bodies, and the scope of each name is managed cleanly. You will meet headers properly in a later rung. For now, build with warnings on — "gcc -Wall main.c" — and treat "implicit declaration of function" as the compiler telling you a prototype is missing, not as noise to ignore.
Recursion: a function that calls itself
Because every call gets its own private copies of the parameters, a function is even allowed to call itself. That is recursion, and it shines on problems that fold neatly into a smaller version of themselves. The factorial of n is n times the factorial of n minus 1 — the same problem, one size smaller — until you hit a size so small the answer is obvious. That obvious case is the base case, and forgetting it is how recursion goes wrong.
- Write the base case first: the smallest input whose answer you can state outright (here, factorial of 0 is 1). Without it the function never stops.
- Write the recursive case: express the answer in terms of the same function on a smaller input (n times factorial of n minus 1).
- Make sure every recursive call moves strictly toward the base case, or it will recurse forever and overflow the call stack.
unsigned long factorial(unsigned n) {
if (n == 0) return 1; /* base case */
return n * factorial(n - 1); /* recursive case */
}main is just a function too
Everything you just learned applies to main(), the function the system calls to start your program. It returns an int — that integer becomes the program's exit status, where 0 means success — and the operating system can hand it arguments from the command line. You will see those written as a count and a list of strings; the exact spelling of that parameter list, including the one that needs a double asterisk, is shown in the glossary, but the idea is simply that the words you type after the program name arrive as arguments to main, exactly like any other function's parameters.
Run "$ ./a.out" and main runs with no extra arguments; run "$ ./a.out hello 42" and those two words show up inside main for you to read. This is the same machinery as max_of, scaled up to the boundary between your program and the world. Once functions click, a program stops being one wall of code and becomes a small society of named pieces, each doing one job and calling on the others — which is exactly the mindset you will need for the structs and the standard library coming next.