Code of the Day
BeginnerFunctions

Declaring and defining functions

Learn how to declare and define functions in C — including the declaration vs definition distinction, function prototypes, and void functions.

CBeginner10 min read
By the end of this lesson you will be able to:
  • Write a function definition with a return type, name, and parameter list
  • Explain the difference between a declaration (prototype) and a definition
  • Call a function and use its return value
  • Write a void function that produces output as a side effect

So far every program has been a single main function that does everything. That approach does not scale. Functions let you name a piece of logic, reuse it, and reason about it independently. They are the primary unit of abstraction in C.

A first function

#include <stdio.h>

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

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

Breaking down the function definition:

  • int — the return type: the type of value this function produces.
  • square — the name: how you call the function.
  • (int x) — the parameter list: values the caller passes in. x is a local copy.
  • { return x * x; } — the body: the code that runs when the function is called.
  • return x * x; — sends the result back to the caller.

Declaration versus definition

In C, you cannot call a function before the compiler has seen it — either a declaration (also called a prototype) or the full definition. The simplest approach is to define functions above main:

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

int main(void) {
    printf("%d\n", add(3, 4)); /* OK: add is above main */
}

The alternative is to separate declaration from definition using a prototype:

/* Declaration: tells the compiler the function exists and its signature */
int add(int a, int b);

int main(void) {
    printf("%d\n", add(3, 4)); /* OK: the prototype was above main */
}

/* Definition: the actual implementation, can come after main */
int add(int a, int b) {
    return a + b;
}

The prototype int add(int a, int b); is a promise to the compiler: "I will define this function, and it will have exactly this signature." In larger programs, prototypes live in header files.

Parameter names in prototypes are optional. int add(int, int); is a valid prototype — the parameter names are just for documentation. In practice, keep the names in prototypes for readability.

void functions

When a function does not return a value, use void as the return type:

#include <stdio.h>

void print_separator(void) {
    printf("-------------------\n");
}

void print_header(const char *title) {
    print_separator();
    printf("  %s\n", title);
    print_separator();
}

int main(void) {
    print_header("Results");
    printf("Score: 95\n");
    return 0;
}

void functions can use return; (with no value) to exit early, but they do not need to — control returns automatically at the end of the body.

Calling conventions

When you call square(7):

  1. The compiler evaluates the argument 7.
  2. A stack frame is pushed — a block of memory for the function's local variables (including x).
  3. The value 7 is copied into x.
  4. The function body executes.
  5. return x * x puts the result where the caller can find it.
  6. The stack frame is popped.
  7. Execution returns to the line after the call.

The key word is copied. In C, arguments are passed by value. Modifying x inside square does not change the original variable in main. This is different from references in languages like Python (where mutable objects can be modified through a function call).

Multiple parameters

#include <stdio.h>

double hypotenuse(double a, double b) {
    return a * a + b * b; /* returns a^2 + b^2; caller takes the sqrt */
}

int max_of_three(int a, int b, int c) {
    int max = a;
    if (b > max) { max = b; }
    if (c > max) { max = c; }
    return max;
}

int main(void) {
    printf("sum of squares: %.1f\n", hypotenuse(3.0, 4.0)); /* 25.0 */
    printf("max: %d\n", max_of_three(7, 2, 9));             /* 9   */
    return 0;
}

Where to go next

Next: parameters and return values in depth — how values are passed into functions, how to return multiple results using pointers, and how to write functions that modify their arguments.

Finished reading? Mark it complete to track your progress.

On this page