C Basics · beginner · ~12 min
Define and call functions; pass parameters by value.
A function is a named, reusable block of code with typed parameters and a typed return value. You define it once and call it from anywhere. Functions are how C programs are decomposed — main is just one function that calls others.
Functions are the most powerful tool you have for managing complexity. A 500-line main is unreadable; ten 50-line functions, each with a clear name and purpose, are. Functions also make code testable (you can call one function with known inputs and check its output), reusable (call it from multiple places), and replaceable (swap the body without changing the callers).
Signature. return_type name(params) is the function's contract. Callers care about it; implementers must respect it. Parameters are copies. When you call f(x), the function gets its own copy of x — modifying the parameter does not change the caller's variable. To mutate the caller's data, take a pointer (void f(int *p)). Return value. A function returns at most one value; use pointers or structs for multiple outputs. Declarations vs definitions. A declaration (int square(int);) tells the compiler the signature; a definition provides the body. Headers contain declarations; .c files contain definitions.
int add(int a, int b); // declaration (prototype)
int add(int a, int b) { // definition
return a + b;
}
void greet(const char *name) { // void return = no value
printf("Hello, %s\n", name);
}
int result = add(2, 3); // call site
A function has a return type, a name, a parameter list, and a body. Call it by name with arguments. Arguments are passed by value — changes to a parameter do not affect the caller. To let a function modify the caller's data, pass a pointer (covered in the Pointers section).
Declare a prototype (int sum(int, int);) in a header so other files can call the function before its definition is seen.
int sum(int a, int b) { return a + b; }
int main(void) { printf("%d\n", sum(2, 3)); }
int when you expected void — and ignoring the return value silently (use (void)-cast to be explicit when you mean to discard).If a function returns the wrong value, print its inputs at the top and its return value at the bottom. If a function appears to have no effect, check whether you took a copy when you needed a pointer. The compiler's -Wall -Wextra will warn about missing return statements and unused parameters.
Never return a pointer to a local variable — its memory is reclaimed when the function exits, and the caller gets a dangling pointer. To return data from a function, either fill a buffer the caller provides, return by value (for small structs), or allocate with malloc and document who must free.
Every API in every C library: printf, strlen, fopen, malloc, pthread_create, socket. Modular code has dozens of small, well-named functions; spaghetti code has three giant ones.
int square(int n) and call it from main. 2. Write a function that swaps two ints (you'll need pointers — see the next chapter). 3. Write a function that returns the larger of two doubles. 4. Refactor a long main function into three smaller ones.Functions decompose a program into named, testable units. Pass by value for inputs, pass by pointer when you need to mutate, never return a pointer to a local. A program of well-named small functions reads almost like prose.