C Functions

A function in C is a named block of code that performs a specific task. Functions matter because they let you split a program into small, understandable pieces instead of putting every statement inside main.

You have already used library functions such as printf. In this lesson, you will define your own functions, call them, return values, and understand what C does when control jumps into and out of a function.

Overview: How C Functions Work

Every C program starts by calling the function named main. From there, your program may call other functions. A function call temporarily transfers execution to the function body. When the function finishes, execution returns to the statement after the call.

A function has a return type, a name, a parameter list in parentheses, and a body in braces. The return type tells the compiler what kind of value the function sends back. A function declared with void as its return type performs an action but does not return a useful value.

Functions also create boundaries in your program. Variables declared inside a function are local to that function. They exist while the function call is active and are normally gone when the function returns. This is one reason functions are useful: each function can have its own temporary variables without interfering with variables in another function.

At compile time, C checks that a function is known before it is called. The compiler needs to know the function’s return type and parameter types so it can check the call correctly. You can satisfy this by defining the function before main, or by writing a function declaration, also called a prototype, before main and placing the full definition later.

Good functions are usually small and focused. A function named printHeader should print a header; a function named getScore should return a score. Clear function names make a program read like a sequence of meaningful steps.

Syntax

This complete program shows the general shape of a function definition and a prototype:

#include <stdio.h>

int functionName(void);

int main(void)
{
    printf("Value: %d\n", functionName());
    return 0;
}

int functionName(void)
{
    return 10;
}

Output:

Value: 10
Part Meaning
return_type The type of value the function returns, such as int, double, char, or void.
function_name The identifier used to call the function. It follows normal C naming rules.
parameter_list The values the function can receive. Use void when the function takes no parameters.
statements The work performed when the function is called.
return Sends a value back to the caller and ends the current function call.

A prototype has the same return type, name, and parameter types, but it ends with a semicolon and has no body, such as int getScore(void);.

Examples

Calling a simple function

#include <stdio.h>

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

int main(void)
{
    printLine();
    printf("Daily Report\n");
    printLine();

    return 0;
}

Output:

--------------------
Daily Report
--------------------

The function printLine takes no parameters and returns no value. The two calls inside main run the same function body twice. This removes duplicated printf statements and gives the repeated action a useful name.

Returning a value

#include <stdio.h>

int getPassingScore(void)
{
    return 70;
}

int main(void)
{
    int score = getPassingScore();

    printf("Passing score: %d\n", score);
    printf("With bonus: %d\n", getPassingScore() + 5);

    return 0;
}

Output:

Passing score: 70
With bonus: 75

The function getPassingScore returns an int. The returned value can be stored in a variable, printed, compared, or used inside a larger expression. When return 70; runs, the function ends immediately and gives 70 back to the call site.

Using a prototype

#include <stdio.h>

int getCurrentYear(void);
void printWelcome(void);

int main(void)
{
    printWelcome();
    printf("Year: %d\n", getCurrentYear());

    return 0;
}

void printWelcome(void)
{
    printf("Welcome to C functions.\n");
}

int getCurrentYear(void)
{
    return 2026;
}

Output:

Welcome to C functions.
Year: 2026

Here, main appears before the full function definitions. The prototypes tell the compiler that printWelcome and getCurrentYear exist, what they return, and that they take no parameters. Without those declarations, a modern C compiler should diagnose the calls before the definitions.

Combining functions in a realistic flow

#include <stdio.h>

int readBaseScore(void);
int getBonus(void);
void printFinalScore(int finalScore);

int main(void)
{
    int baseScore = readBaseScore();
    int bonus = getBonus();
    int finalScore = baseScore + bonus;

    printFinalScore(finalScore);

    return 0;
}

int readBaseScore(void)
{
    return 82;
}

int getBonus(void)
{
    return 8;
}

void printFinalScore(int finalScore)
{
    printf("Final score: %d\n", finalScore);
}

Output:

Final score: 90

This program is still small, but it shows a useful habit: main describes the high-level flow while helper functions handle separate tasks. The parameter in printFinalScore receives the final number to print. Parameters are covered more deeply in the next lesson, but this example shows how functions work together.

How It Works Step By Step

When the program starts, the C runtime calls main. Inside main, each function call evaluates its arguments first, then transfers control to the called function. The called function gets its own execution context, often described as a stack frame. That context stores information such as local variables, parameter values, and where execution should return afterward.

For example, in int score = getPassingScore();, the program enters getPassingScore. The statement return 70; produces an integer result and ends that call. Control returns to main, and the result is assigned to score.

A non-void function must return a value that is compatible with its declared return type. Returning a double from a function declared as int may convert the value and lose information. Reaching the end of a non-void function without returning a value is undefined behavior if the caller tries to use the result.

A void function may use return; with no value to exit early, or it may simply reach the closing brace. It must not return a value because the caller is not expecting one.

Common Mistakes

Calling a function before declaring it

C needs a declaration before a call. This call is a mistake if the compiler has not already seen a definition or prototype for getAnswer. The corrected version places a prototype before main:

#include <stdio.h>

int getAnswer(void);

int main(void)
{
    printf("Answer: %d\n", getAnswer());
    return 0;
}

int getAnswer(void)
{
    return 42;
}

Output:

Answer: 42

The prototype int getAnswer(void); is a promise about the function’s interface. The later definition must match that promise.

Forgetting to return a value

If a function promises to return an int, every normal path should return an int. Do not write a non-void function that performs work but falls off the end. A corrected version returns the computed result explicitly:

#include <stdio.h>

int square(void)
{
    int value = 6;
    return value * value;
}

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

Output:

Square: 36

The return statement makes the function’s contract complete: callers can rely on receiving an integer result.

Confusing a function call with a function name

The parentheses are part of the call. Writing only the function name refers to the function itself, not the act of running it. Use parentheses when you want the body to execute:

#include <stdio.h>

void showReady(void)
{
    printf("Ready\n");
}

int main(void)
{
    showReady();
    return 0;
}

Output:

Ready

The statement showReady(); calls the function. The semicolon ends the call statement.

Best Practices

  • Give each function one clear job, and name it after that job.
  • Use void in an empty parameter list, such as int getCount(void), to clearly say that no arguments are accepted.
  • Write prototypes near the top of the file when function definitions appear after main.
  • Keep return types honest. If a function calculates a value, return it; if it only performs an action, use void.
  • Prefer readable function names such as printMenu or calculateTotal over vague names such as doStuff.
  • Avoid very large functions. When a function becomes hard to scan, split part of its work into a helper function.
  • Compile with warnings enabled, such as -Wall -Wextra, so mismatched declarations and missing returns are easier to catch.

Practice Exercises

  1. Write a function named printTitle that prints a two-line title, then call it from main.
  2. Write a function named getTaxRate that returns 7, then print Tax rate: 7% from main.
  3. Write prototypes for two functions, define them after main, and call both from main. One function should return an int, and the other should return void.

Summary

  • A function is a named block of C code that can be called from another part of the program.
  • A function definition includes a return type, name, parameter list, and body.
  • void means a function returns no value, or accepts no parameters when used in the parameter list.
  • A non-void function should return a value compatible with its return type.
  • A prototype lets the compiler check calls before it has seen the full function definition.
  • Function calls create temporary execution contexts for local work, then return control to the caller.