Skip to main content

Functions

Functions

Master C Functions — what/why, types of functions, types of calling a user-defined function, and the function prototype — using LEARNCODEGURU’s 6-level learning model.

Level 1 Basics Content

C Functions Overview

A function is a named block of code that performs one task. You write it once and call it as many times as you need. This level covers what a function is, why functions matter, types of functions, the four types of calling a user-defined function, the function prototype, definition, and calling.

Every C program already uses functions: main() is a function, and printf() / scanf() are library functions you call.

Topic 1

What is a function?

A function groups statements under a name, optionally accepts parameters (inputs) and optionally returns a value (output).


int add(int a, int b) {   /* return type, name, parameters */
    return a + b;         /* body — the work */
}
  • Return type — type of value sent back (void if none)
  • Name — identifier used to call it
  • Parameters — inputs declared in the brackets
  • Body — statements inside { }
  • return — sends a value back and ends the function
Declare prototype → define body → call from main → value returns to the caller
Topic 2

Why functions?

  • Reusability — write once, call many times
  • Readability — main() becomes a short summary of steps
  • Divide and conquer — break a big problem into small tasks
  • Easier testing / debugging — check one small function at a time
  • Less duplication — fix a bug in one place, not ten
  • Teamwork — different people can write different functions
Rule of thumb: if you copy-paste the same few lines a third time, turn them into a function.
Topic 3

Types of functions

C functions fall into two broad groups:

# Type Who writes it Where it lives Examples
1 Library (built-in) functions Provided by the C standard library Declared in headers like <stdio.h>, <math.h>, <string.h> printf, scanf, sqrt, strlen
2 User-defined functions You, the programmer In your own .c file(s) add, findMax, printMenu

User-defined functions are further grouped by arguments and return value:

# Category Signature example Use when
1 No arguments, no return void greet(void); Fixed action like printing a banner
2 With arguments, no return void printArr(int a[], int n); Needs input, produces only output on screen
3 No arguments, with return int readNumber(void); Gets data itself and hands back a value
4 With arguments, with return int add(int a, int b); Most common — compute a result from inputs
Topic 4

Types of calling a user-defined function

Two things are commonly meant by “types of calling”: the four argument/return combinations above, and the mechanism used to pass data.

(a) Call by value — a copy is sent; the original is unchanged:


void change(int x) {
    x = 100;          /* only the copy changes */
}

int n = 5;
change(n);              /* n is still 5 */

(b) Call by reference (via pointers) — an address is sent; the original changes:


void change(int *p) {
    *p = 100;         /* writes through the address */
}

int n = 5;
change(&n);             /* n becomes 100 */
Aspect Call by value Call by reference
What is passed Copy of the value Address of the variable
Caller variable Unchanged Can be modified
Syntax at call f(n) f(&n)
Parameter type int x int *p
Best for Simple computations Multiple results, in-place edits, large data
Strictly speaking C only has call by value — “call by reference” is call by value of a pointer. That phrasing wins interview marks.
Topic 5

Function prototype (declaration)

A prototype tells the compiler the function’s name, return type, and parameter types before it is used, so calls can be checked.


int    add(int a, int b);   /* prototype — ends with ; */
void   greet(void);            /* takes no arguments */
double area(double r);        /* returns a double */
int    add(int, int);         /* names are optional */
Part Prototype Definition Call
Purpose Announce the signature Provide the body Execute it
Ends with ; { ... } ; (as a statement)
How many times Many (identical) allowed Exactly once Any number of times
Example int add(int, int); int add(int a, int b) { ... } s = add(2, 3);
Tip: use void in the brackets for “no parameters”: void greet(void); — not void greet();.
Topic 6

Definition, call, and return


return_type name(parameter list) {
    /* local declarations */
    /* statements */
    return value;   /* omit for void */
}
  • Actual arguments — values you pass at the call: add(2, 3)
  • Formal parameters — names in the definition: int a, int b
  • return — ends the function immediately and sends a value back
  • void functions may use a bare return; to exit early
A call is an expression: you can use it in assignments (s = add(2,3);), conditions (if (isEven(n))), or discard the result.
Topic 7

Local vs global — scope basics

  • Local variables — declared inside a function; exist only during the call
  • Parameters — behave like locals initialized from the arguments
  • Global variables — declared outside all functions; visible everywhere (use sparingly)
  • Two functions may use the same local name without any clash
Example

Sample program — prototype, definition, and calls


/* Functions basics: prototype above main, definitions below */
#include <stdio.h>

void greet(void);              /* no args, no return */
int  add(int a, int b);      /* args + return */
void doubleIt(int *p);        /* call by reference */

int main(void) {
    int x, y, sum;

    greet();

    printf("Enter two integers: ");
    scanf("%d %d", &x, &y);

    sum = add(x, y);
    printf("Sum = %d\n", sum);

    doubleIt(&x);
    printf("x after doubleIt = %d\n", x);

    return 0;
}

void greet(void) {
    printf("=== LEARNCODEGURU C Functions ===\n");
}

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

void doubleIt(int *p) {
    *p = *p * 2;
}
What to observe: greet takes nothing and returns nothing; add uses call by value; doubleIt receives an address and changes x in main.

Level 2 Advanced Content

Going deeper into functions

Level 1 covered what/why, types, calling styles, and prototypes. Level 2 explains the stack frame, passing arrays and strings, returning values safely, recursion, static / extern linkage, multi-file organisation, and common mistakes.

Topic 1

How a call works — the stack frame

Each call pushes a frame holding the parameters, local variables, and the return location. When the function returns, its frame is popped — which is why locals do not survive the call.

main frame → push add() frame (a, b, locals) → compute → return value → pop frame
  • Deep call chains and recursion consume stack space
  • Large local arrays live in the frame — huge ones can overflow the stack
  • Return value travels back to the caller’s expression
Topic 2

Passing arrays, strings, and structs


int findMax(int a[], int n);        /* 1D array + length */
void printMat(int m[][3], int rows); /* 2D: columns must be known */
int countVowels(const char s[]);   /* string ends at '\0' */
  • Arrays decay to pointers — the function can modify the caller’s data
  • Strings need no length parameter (the '\0' marks the end)
  • Structs are copied by value unless you pass struct T *
  • Use const on parameters you promise not to modify
Topic 3

Return values — rules and traps

Situation Allowed? What to do
Return one value Yes Use return expr;
Return two values Not directly Use pointer parameters or return a struct
Return an array No Return a pointer to valid memory, or fill a caller buffer
Return address of a local Undefined behavior Use static, heap, or caller-provided storage
Non-void with no return Undefined if the value is used Always return on every path
Topic 4

Recursion — a function calling itself


long fact(int n) {
    if (n <= 1)
        return 1;        /* base case — stops recursion */
    return n * fact(n - 1);  /* recursive case */
}
  • Every recursion needs a base case and progress toward it
  • Each level uses its own stack frame — deep recursion can overflow
  • Iteration is often faster; recursion is clearer for trees and divide-and-conquer
Topic 5

Storage & linkage — static, extern, inline

Keyword On a function On a local variable
static Visible only in this file (internal linkage) Keeps its value between calls
extern Declares a function defined elsewhere (default for functions) Refers to a global defined in another file
inline (C99) Hint to expand the call in place Not applicable

int counter(void) {
    static int count = 0;  /* initialized once; survives calls */
    count++;
    return count;
}
Topic 6

Organising functions across files

  • Put prototypes in a header (.h) file
  • Put definitions in a source (.c) file
  • #include "myheader.h" wherever you call those functions
  • Mark file-private helpers static to avoid name clashes
One definition per function across the whole program — duplicate definitions cause linker errors like “multiple definition of …”.
Topic 7

Common mistakes

Mistake Why it hurts Fix
Calling before declaring No argument checking / invalid in modern C Add a prototype above main
Semicolon after the definition header int f(int a); { ... } — becomes a stray declaration No ; before the body brace
Expecting call by value to change the caller Original variable stays the same Pass &x and use *p
Prototype and definition disagree Type mismatch / conflicting types error Keep return type and parameter types identical
Using sizeof on an array parameter Gives pointer size, not length Pass the length explicitly
Recursion without a base case Stack overflow at runtime Handle the smallest case first

Level 3 C Tricky Interview Points

Advanced tricky interview points on prototypes, call by value vs reference, array decay, return rules, linkage, and recursion.

Tricky Interview Point-1:

A prototype declares; a definition provides the body

int add(int, int); is a declaration (prototype) ending with a semicolon. int add(int a, int b) { ... } is the definition. You may declare many times but define exactly once in a program.

Tricky Interview Point-2:

Missing prototype before use is a real bug, not just a warning

Since C99, calling an undeclared function is invalid. Older compilers assumed int return and unchecked arguments, which silently broke float/double returns. Always declare before the first call or define above main.

Tricky Interview Point-3:

C is call by value — always

Arguments are copied into parameters. Changing a parameter inside the function does not affect the caller's variable. "Call by reference" in C is simulated by passing addresses (pointers) — the pointer itself is still passed by value.

Tricky Interview Point-4:

void f() and void f(void) are not the same in C

In C, f() means "unspecified parameters" (no argument checking in old rules), while f(void) means "takes no arguments." Use (void) explicitly. In C++ they are equivalent — a common cross-language trap.

Tricky Interview Point-5:

Arrays passed to a function decay to pointers

void f(int a[]) is really void f(int *a). sizeof(a) inside gives the pointer size, not the array size, so pass the length separately. Changes to a[i] inside DO affect the caller's array.

Tricky Interview Point-6:

Returning a pointer to a local variable is undefined behavior

The local's lifetime ends when the function returns, so the pointer dangles. Return by value, use a caller-provided buffer, use static storage, or allocate on the heap.

Tricky Interview Point-7:

Falling off the end of a non-void function is undefined use

If a function is declared int but reaches the closing brace without return, the value returned is indeterminate and using it is undefined. main is special: reaching the end of main implies return 0 since C99.

Tricky Interview Point-8:

Default argument promotions apply where no prototype is in scope

Without a prototype, float promotes to double and small integer types to int. That is why a printf-style call with mismatched types misbehaves — variadic functions rely on promotions and the format string.

Tricky Interview Point-9:

Parameter names in a prototype are optional and ignored

int add(int, int); and int add(int x, int y); declare the same function. Names in prototypes are documentation only; only the types (and order) matter for compatibility.

Tricky Interview Point-10:

A function cannot return an array, but it can return a pointer or struct

int f()[10] is invalid. You can return int * (pointing to valid memory) or return a struct by value that wraps the data. This is a favorite interview distinction.

Tricky Interview Point-11:

static on a function limits it to the current file

static int helper(void) has internal linkage — invisible to other translation units. Non-static functions are external by default and can clash across files if names repeat.

Tricky Interview Point-12:

Recursion needs a base case and consumes stack per call

Every recursive call adds a frame with its own parameters and locals. Without a base case (or with a wrong one) you get infinite recursion and stack overflow, not a compile error.

Interview tip: state clearly that C passes everything by value, and that pointers are how you simulate call by reference.

Level 4 Simple Programs

Short programs covering the four calling categories, call by value vs reference, arrays, and recursion.

Program 1 — All four argument/return categories


/* no-arg/no-return, arg/no-return, no-arg/return, arg/return */
#include <stdio.h>

void banner(void);
void showSquare(int n);
int  readNumber(void);
int  add(int a, int b);

int main(void) {
    int a, b;

    banner();
    a = readNumber();
    b = readNumber();

    showSquare(a);
    printf("a + b = %d\n", add(a, b));

    return 0;
}

void banner(void) {
    printf("--- Function Demo ---\n");
}

void showSquare(int n) {
    printf("Square of %d = %d\n", n, n * n);
}

int readNumber(void) {
    int v;
    printf("Enter a number: ");
    scanf("%d", &v);
    return v;
}

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

Program 2 — Call by value vs call by reference (swap)


/* Only the pointer version changes main's variables */
#include <stdio.h>

void swapValue(int x, int y);
void swapRef(int *x, int *y);

int main(void) {
    int a = 10, b = 20;

    swapValue(a, b);
    printf("After swapValue: a=%d b=%d\n", a, b);

    swapRef(&a, &b);
    printf("After swapRef:   a=%d b=%d\n", a, b);

    return 0;
}

void swapValue(int x, int y) {
    int t = x; x = y; y = t;   /* copies only */
}

void swapRef(int *x, int *y) {
    int t = *x; *x = *y; *y = t;
}

Program 3 — Functions with an array (read, max, print)


/* Array parameters always need the length */
#include <stdio.h>

void readArray(int a[], int n);
int  findMax(int a[], int n);
void printArray(int a[], int n);

int main(void) {
    int a[100], n;

    printf("Enter n: ");
    scanf("%d", &n);

    readArray(a, n);
    printArray(a, n);
    printf("Max = %d\n", findMax(a, n));

    return 0;
}

void readArray(int a[], int n) {
    int i;
    printf("Enter %d integers:\n", n);
    for (i = 0; i < n; i++)
        scanf("%d", &a[i]);
}

int findMax(int a[], int n) {
    int i, max = a[0];
    for (i = 1; i < n; i++)
        if (a[i] > max)
            max = a[i];
    return max;
}

void printArray(int a[], int n) {
    int i;
    printf("Array: ");
    for (i = 0; i < n; i++)
        printf("%d ", a[i]);
    printf("\n");
}

Program 4 — Factorial: loop vs recursion


/* Same result, two styles */
#include <stdio.h>

long factLoop(int n);
long factRec(int n);

int main(void) {
    int n;

    printf("Enter n: ");
    scanf("%d", &n);

    printf("Loop:      %ld\n", factLoop(n));
    printf("Recursive: %ld\n", factRec(n));

    return 0;
}

long factLoop(int n) {
    long f = 1;
    int i;
    for (i = 2; i <= n; i++)
        f *= i;
    return f;
}

long factRec(int n) {
    if (n <= 1)
        return 1;
    return n * factRec(n - 1);
}
Comment Directive Type Keyword Function Variable String Number

Level 5 Problem Solving Tricks

How to master functions while solving problems

Most function bugs come from missing prototypes, expecting call by value to change the caller, losing array lengths, and forgetting a return on some path. These habits keep your programs modular and predictable.

Trick 1

Decide the signature before writing the body

Ask: what does it need (parameters), what does it give back (return type), does it change caller data (pointers)? Writing the prototype first prevents half the bugs.

Trick 2

One function, one job

If you cannot name the function with a single verb phrase, split it. Small functions like readArray, findMax, printArray are easier to test than one giant main.

Trick 3

Use call by reference only when you must change the caller's data

Need two results, or must modify a variable in place? Pass &x and use *p. Otherwise pass by value — it is simpler and safer.

Trick 4

Always pass array length alongside the array

void printArr(int a[], int n). Inside the function sizeof cannot tell you the length. For 2D, pass the column count in the type and rows as a parameter.

Trick 5

Put prototypes above main, definitions below

This layout keeps main readable as a summary of your program and guarantees every call sees a declaration first. It also makes ordering of definitions irrelevant.

Trick 6

Validate inputs at the top of the function

Check n > 0, pointer != NULL, divisor != 0 and return an error code early. Guard clauses keep the main logic flat and avoid crashes deep inside.

Trick 7

Return a status, output through parameters when needed

A common C pattern: int parse(const char *s, int *out) returns 0/1 for success and writes the value through out. It separates "did it work" from "what is the value".

Trick 8

Trace one call by hand — draw the stack frame

Write the parameter values, locals, and the return value for a single call. For recursion, draw two or three levels. Most logic errors become obvious on paper.

Quick success checklist

  • Before run: prototype above main? types match definition? every path returns?
  • Debug order: print arguments on entry → check the return value → trace one frame on paper
  • Design habit: one job per function → pass length with arrays → use pointers only to modify
Level-5 goal: design the signature first, keep functions small and single-purpose, and always know whether you are passing a copy or an address.

Level 6 Practice Programs List

Programming questions only — focused on functions: prototypes, the four calling categories, call by value vs reference, array parameters, and recursion.

  1. 1. Write a C program with a function add(int, int) that returns the sum of two integers read in main.
  2. 2. Write a C program with a void function that takes no arguments and prints a welcome banner (function with no args, no return).
  3. 3. Write a C program with a function isEven(int) that returns 1 or 0, and use it in main to classify n numbers.
  4. 4. Write a C program with a function swapValue(int, int) and swapRef(int *, int *) to demonstrate call by value vs call by reference.
  5. 5. Write a C program with a function that computes the factorial of n, first using a loop and then using recursion.
  6. 6. Write a C program with functions readArray(int[], int), findMax(int[], int), and printArray(int[], int) called from main.
  7. 7. Write a C program with a function that returns the area of a circle as a double, using a prototype declared above main.
  8. 8. Write a C program with a function countVowels(char[]) that returns how many vowels a string contains.