C Mastery / Functions: Declaration, Definition, Parameters, Returns
Part 1 — The Core Language

Functions: Declaration, Definition, Parameters, Returns

This chapter covers how functions are declared and defined, how parameters and arguments interact, and how return values work. Functions are the primary unit of code organization in C.

Why This Matters

C has no methods, closures, or modules. Functions — and their declarations — are how you build abstractions and organize programs across files. The parameter/argument model (pass-by-value) is fundamental to every other concept.

Prerequisites

Core Concept

Declaration (prototype) vs. definition

int add(int a, int b);        /* declaration (prototype) */

int add(int a, int b) {       /* definition */
    return a + b;
}

A prototype declares the return type and parameter types. The definition provides the body.

Pass-by-value

C passes all arguments by value: the parameter is a copy of the argument. Modifying a parameter inside the function does not affect the caller's variable. To modify a caller's variable, pass its address (a pointer).

Return

return expr; returns a value (converted to the function's return type). return; (no value) is used in void functions. Reaching the end of a non-void function without returning is undefined behavior (except main, as noted in c.core.1).

Syntax

return_type function_name(parameter_type name, parameter_type name, ...)
{
    /* body */
    return value;
}

Parameter names are optional in prototypes but required in definitions (unless unused, in which case you can omit the name in the definition too, though most code keeps it and marks it unused).

Examples

Basic function

#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}

int main(void)
{
    printf("%d\n", add(2, 3));
    return 0;
}

Expected output: 5.

Pass-by-value demonstration

#include <stdio.h>

void set_to_five(int x)
{
    x = 5;   /* only the local copy changes */
}

void really_set(int *p)
{
    *p = 5;  /* writes through the pointer */
}

int main(void)
{
    int a = 0;
    set_to_five(a);
    printf("%d\n", a);   /* 0 */
    really_set(&a);
    printf("%d\n", a);   /* 5 */
    return 0;
}

Expected output: 0 then 5.

Function returning a struct (by value)

struct Point { int x; int y; };

struct Point make_point(int x, int y)
{
    struct Point p = { x, y };
    return p;   /* returns a copy */
}

Returning a struct by value is legal and copies the whole struct.

How It Works

At the call site, arguments are evaluated (in unspecified order) and their values are copied into the parameters. The function executes, and on return the return value is copied back (for non-void functions). The details of where parameters and return values live (registers vs. stack) are the ABI's concern (c.build.calling-convention).

Variations

Old-style (K&R) function definitions

int add(a, b)
int a;
int b;
{
    return a + b;
}

This is the pre-ANSI style. It is obsolescent and should not be used in new code. Prototypes (ANSI style) are preferred.

Function with no parameters

int f(void);       /* takes no arguments */
int g();           /* unspecified arguments (avoid) */

Always use (void) for "no parameters."

Functions with static

static functions have internal linkage and are visible only within their translation unit (c.lang.linkage).

Common Mistakes

undeclared function is a constraint violation; in C89 it was allowed with implicit int).

Undefined Behavior

undefined behavior (except main in C99+). VERIFIED

Portability

Under the Hood

The ABI defines how arguments are passed (in registers for the first few arguments on many platforms, then on the stack) and how return values are returned (in a register or on the stack for large structs). This is covered in c.build.4.

Practical Usage

results or modify a caller's object.

opaque data.

Exercises

1. Write a function swap that exchanges two ints using pointers, and verify it works. 2. Demonstrate pass-by-value by showing that a function modifying its parameter does not change the caller's variable. 3. Write a function that returns a struct and inspect what happens to a large struct (use sizeof and observe the copy). 4. Deliberately omit a return from a non-void function and run it with warnings and UBSan.

Deep Challenge

Explain, using the pass-by-value model and pointer semantics, how to write a function that allocates a buffer and returns it to the caller, along with an error code — without using global state. Compare the "return pointer + status" and "output parameter" approaches and discuss ownership.

References

declarators), §6.5.2.2 (function calls).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.func.decl05
c.func.param05
c.func.return05