Skip to content
Functions
step 1/5

Reading — step 1 of 5

Learn

~2 min readFunctions and Pointers

Functions

C functions are the plainest in this catalog — no overloading, no default arguments, no methods, no closures. What they have instead is one rule with deep consequences (everything passes by copy) and one historical requirement (declare before use) that explains the shape of every C file you'll ever read.

The shape

int square(int n) {
    return n * n;
}

int main(void) {
    printf("%d\n", square(7));    /* 49 */
    return 0;
}

Return type, name, typed parameters. void means "nothing here" — void greet(void) takes nothing, returns nothing. A non-void function must return a value on every path; falling off the end returns garbage (UB), and -Wall warns.

Declare before use

The compiler reads top to bottom and must know a function's signature before the first call. Define square below main and the call misfires (implicit-declaration warnings, wrong assumptions, link errors). Two fixes, both idiomatic:

int square(int n);       /* a PROTOTYPE: signature now, body later */

int main(void) {
    printf("%d\n", square(7));
}

int square(int n) {      /* definition, anywhere below */
    return n * n;
}

…or simply define helpers above main. Header files — coming in the Preprocessor lesson — are entire files of prototypes, and now you know why they exist: they let separately-compiled files promise each other signatures.

Everything is a copy

void tryToDouble(int n) {
    n *= 2;              /* modifies the copy */
}

int x = 5;
tryToDouble(x);
printf("%d\n", x);       /* 5 — untouched */

Arguments arrive by value — the function owns private copies. Return the new value (x = doubled(x);) when one result suffices. When a function genuinely must modify the caller's variable — or return more than one thing — C's answer is to pass the address instead of the value: exactly what you've been doing with scanf(&x) all course. Next lesson makes that mechanism yours; this lesson's job is to make the copy rule visceral, because pointers only make sense as the deliberate exception to it.

One nuance to bank for later: arrays are the odd ones out — passing an array actually passes its address (arrays "decay" to pointers at the call boundary), which is why f(int arr[], int n) can modify the caller's elements and why the length must ride along as its own parameter. It'll feel inconsistent until pointers make it feel inevitable.

Your exercise: Square It

int square(int n) plus a main that reads, calls, prints:

int square(int n) {
    return n * n;
}

int main(void) {
    int n;
    scanf("%d", &n);
    printf("%d\n", square(n));
    return 0;
}

The graded muscle is the split — logic in the function, I/O in main — the same discipline as every track on this platform, and in C it has an extra payoff: functions that touch no I/O and take everything by value are the easiest code on earth to reason about, which in a language with this many sharp edges is not a small thing.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…

Functions — C Fundamentals