Functions in C: Syntax, Parameters and Worked Examples

Learn how C declarations, calls, and definitions fit together. Compile two complete programs, follow their values, and trace composition and recursion.

KnowledgeGate Team

Exam prep & CS education

Updated 6 Oct 20266 min read

A C function seems to appear in three places: the declaration, the call, and the definition. Beginners often mix up what each line does and which names hold the values. Runnable programs, value-by-value traces, the pass-by-value rule, and a small recursion trace illustrate how C functions work.

Use the broader Functions in C Programming: Complete Guide with Worked Examples for arrays as parameters, storage duration, and recursion cost. First master the narrower mechanics: compile prototypes, calls and definitions; classify four signature shapes; trace value copies; compose small functions; and diagnose interface errors.

What a function is, and the three pieces C needs

A function is a named block of code with a return type, a name, zero or more parameters, and a body. In int add(int a, int b), int is the return type, add is the name, and a and b are formal parameters.

The same function takes three forms in a program:

Form

C code

What it contributes

Declaration

int add(int a, int b);

Gives the compiler the function's interface

Call

total = add(12, 8);

Supplies actual arguments and transfers control

Definition

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

Contains the work the function performs

main is also a function. The standard beginner form int main(void) says that it returns an integer and takes no parameters. Functions such as printf come from the standard library, while functions such as add are written by you.

First runnable example: add 12 and 8

Save this complete program as functions.c:

c
#include <stdio.h>

int add(int a, int b);

int main(void) {
    int first = 12;
    int second = 8;
    int total = add(first, second);

    printf("%d + %d = %d\n", first, second, total);
    return 0;
}

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

Compile and run it:

Code
cc -std=c17 -Wall -Wextra functions.c -o functions
./functions

The output is:

Code
12 + 8 = 20

Trace the execution in order. first holds 12 and second holds 8. The call copies those values into a = 12 and b = 8. The expression a + b becomes 12 + 8 = 20. return sends 20 back to the caller, and total receives 20.

At the call site, first and second are arguments. Inside add, a and b are parameters.

Call-flow diagram: main copies first = 12 and second = 8 into add, which returns a + b = 20 to total and prints 12 + 8 = 20.

Parameters and return values: the four basic shapes

A function can take parameters, return a value, do both, or do neither.

Signature

Example call

Result

void greet(void)

greet()

Prints Hello, C functions!

void printSum(int a, int b)

printSum(12, 8)

Prints 20

int passingMark(void)

int mark = passingMark()

Returns 40

int multiply(int a, int b)

multiply(6, 7)

Returns 42

void before the function name means that no value is returned. void inside the parameter list explicitly means that the function takes no parameters. A non-void function must return a value compatible with its declared return type.

Print inside a void function when displaying something is the function's job. Return a value when the caller may need to store, compare, or combine the result.

C passes ordinary arguments by value

Passing by value means that a function receives a copy of an ordinary argument:

c
#include <stdio.h>

int addFive(int n);

int main(void) {
    int x = 10;
    int y = addFive(x);

    printf("x = %d, y = %d\n", x, y);
    return 0;
}

int addFive(int n) {
    n = n + 5;
    return n;
}

Its output is x = 10, y = 15. The call creates a local parameter with n = 10. The function changes only that copy, so n = 10 + 5 = 15, then returns 15 into y. The caller's variable x remains 10.

n exists only while addFive runs. x and y belong to main. C does not pass ordinary arguments by reference. Modifying a caller's object through its address is a later pointers lesson.

Build larger work by composing small functions

One function can call another:

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

int sumOfSquares(int a, int b) {
    return square(a) + square(b);
}

For sumOfSquares(3, 4), square(3) = 3 * 3 = 9 and square(4) = 4 * 4 = 16. The outer function then returns 9 + 16 = 25.

This is clearer than copying the arithmetic into main. Each function has one job, repeated logic has one definition, and square can be tested independently. Inputs 0, 3, and -5 return 0, 9, and 25 respectively.

Source order is flexible when declarations appear before calls. In a growing program, place declarations such as int square(int n); and int sumOfSquares(int a, int b); above main, then place both definitions below it.

Recursion: trace sumTo(4) down and back up

A recursive function calls itself with a smaller problem:

c
int sumTo(int n) {
    if (n <= 0) return 0;
    return n + sumTo(n - 1);
}

For the non-negative input 4, sumTo(4) waits for sumTo(3), which waits for sumTo(2), which waits for sumTo(1), which waits for sumTo(0).

The base case returns 0 and stops new calls. The waiting additions then unwind:

  1. sumTo(0) = 0

  2. sumTo(1) = 1 + 0 = 1

  3. sumTo(2) = 2 + 1 = 3

  4. sumTo(3) = 3 + 3 = 6

  5. sumTo(4) = 4 + 6 = 10

Each waiting call occupies a call-stack frame. sumTo demonstrates how a function postpones addition until a deeper call returns. Basic Recursion in C: Call Stack, Worked Traces and Exam Patterns develops call counts, output order, time and stack space, and recursion-versus-iteration choices. Without a base case, recursion keeps creating calls until execution fails, so there is no normal result to predict.

Common function errors and how to fix them

Calling before a declaration. Writing int total = add(12, 8); before any declaration of add is a compile-time constraint violation in modern C. Put int add(int, int); before main, or move the full definition above main.

Mismatching the declaration and definition. The prototype int add(int, int); conflicts with double add(double a, double b). Make both use one interface. Do not cast away the disagreement.

Forgetting a return path. An int isPositive(int n) that returns only when n > 0 is incomplete for zero and negative inputs. The complete body is if (n > 0) return 1; return 0;.

Confusing a name with a call. total = add; does not call the function. total = add(12, 8); does. Other logic errors include omitting a recursion base case and expecting addFive(x) to mutate x.

How questions test functions

Answer these before running any code:

  1. With int twice(int x) { return 2 * x; }, twice(3) + twice(4) becomes 6 + 8 = 14.

  2. With int bump(int x) { return x + 1; } and n = 5, bump(n) returns 6 while n stays 5.

  3. sumTo(4) returns 10.

For each check, identify the argument, parameter, return value, and caller variable. Common exam questions include tracing a short call, distinguishing formal parameters from actual arguments, finding a prototype mismatch, applying pass by value, and unwinding recursion to its base case.

The short version

A declaration describes the interface. A call copies ordinary argument values and transfers control. The definition executes, and return sends one result to the caller.

Continue with the C Language course for sequenced C practice. Use the CS Fundamentals for Placements course to connect C with the wider placement core, or browse the Coding & Skills category.