C Basics · beginner · ~12 min

Functions

Define and call functions; pass parameters by value.

Overview

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.

Why it matters

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).

Core concepts

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.

Syntax notes

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

Lesson

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.

Code examples

int sum(int a, int b) { return a + b; }
int main(void) { printf("%d\n", sum(2, 3)); }

Common mistakes

  • Calling a function that returns int when you expected void — and ignoring the return value silently (use (void)-cast to be explicit when you mean to discard).
  • Mismatched prototype vs. definition: changing one without the other.

Debugging tips

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.

Memory safety

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.

Real-world uses

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.

Practice tasks

  1. Write 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.

Summary

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.

Practice with these exercises