f(x)
Functions in C — Part 1
🏠 Home Lecture 16: Functions Basics
Lecture 16 · Functions in C

Why Do We Need Functions?

Imagine copy-pasting the same 20 lines again and again. Functions let you define once, call many times — the foundation of structured programming in C.

What happens if you don’t use functions and need factorial at 5 different places in a 1000-line program?
problem.c❌ BAD APPROACH
// Factorial repeated at 3 places — nightmare! // Place 1: int f1=1, i; for(i=1; i<=5; i++) f1 *= i; printf("%d\n", f1); // Place 2: int f2=1, j; for(j=1; j<=6; j++) f2 *= j; printf("%d\n", f2); // Bug found in formula? Fix in EVERY place! // One bug = triple the debugging pain.
♻️

Reusability

Write once, use anywhere. No copy-pasting code blocks.

🧩

Modularity

Break a big problem into smaller, manageable sub-problems.

🐛

Easy Debugging

Bug in factorial? Fix it in one function — fixed everywhere.

📖

Readability

calculateArea(r) is clearer than 5 raw lines of math.

Syntax Deep Dive

Anatomy of a C Function

Every function has 4 essential parts. Hover over each highlighted part to learn what it does:

factorial.c 💡 Hover over any highlighted word
// Hover each colored part to learn its role! int factorial(int n) { int result = 1; for(int i=1; i<=n; i++) result *= i; return result; }

Hover: int  |  factorial  |  (int n)  |  { body }  |  return

Two Key Concepts

Declaration vs. Definition

These are two different things that beginners often confuse — understand them clearly.

declaration.c📋 DECLARATION
// Just the signature — no body! // A PROMISE to the compiler: // "This function exists, trust me." int factorial(int n); // note semicolon! void greet(void); float area(float r);
📋 Declaration = signature only + semicolon. No code, no curly braces.
definition.c✅ DEFINITION
// Definition has the full body! int factorial(int n) { // no semicolon! int result = 1; for(int i=1; i<=n; i++) result *= i; return result; }
✅ Definition = full function with logic inside { }.
💡 Rule: A function can be declared many times but defined only once. A definition also acts as a declaration if it appears before the first use.
Forward Declaration

Function Prototype — Why It Matters

C reads code top to bottom. If you call a function before defining it, the compiler panics — unless you give it a prototype first.

no_prototype.c❌ COMPILER ERROR
#include <stdio.h> int main() { // ERROR! "add" defined BELOW main int sum = add(3, 4); printf("%d\n", sum); return 0; } int add(int a, int b) { return a + b; }
❌ Error: “implicit declaration of function ‘add’”
with_prototype.c✅ CORRECT
#include <stdio.h> // ✅ Prototype at top — compiler knows! int add(int a, int b); int main() { int sum = add(3, 4); printf("%d\n", sum); // Output: 7 return 0; } int add(int a, int b) { return a + b; }
✅ Prototype tells the compiler types before it sees the definition.
📍

Where to place it?

Before main(), usually at the top of the file or inside a .h header file.

🏷️

Names are optional

int add(int, int); is a valid prototype — parameter names can be omitted.

🗂️

Header Files

#include <stdio.h> gives you prototypes for printf, scanf, etc.

Control Flow

Calling a Function — What Happens Inside?

When you call a function, control jumps to it. After it finishes, control returns back to exactly where it left off.

calling.cStep through →
int square(int x) { int result = x * x; return result; } int main() { int num = 5; int ans = square(num); printf("%d\n", ans); return 0; }

🔍 What’s Happening?

Press ▶ Next Step to begin…
Ready. Step through the code to trace execution.
Variable Watch
— waiting for execution —
Scope & Lifetime

Variable Scope — Who Can See What?

A variable’s scope is where it can be accessed. Each function creates its own private space — variables inside it are invisible to outsiders.

🌍 Global Scope
countint0
PIfloat3.14

Declared outside all functions. Visible to every function. Lives for the entire program duration. Auto-initialized to 0.

🔒 Local Scope (inside main)
numint5
ansint25

Declared inside main(). Only visible inside main(). Destroyed when main() returns. Contains garbage if not initialized.

⚠️ Shadowing (same name!)
countint (local)99

A local variable named count shadows (hides) the global count within that function. Dangerous — avoid same names!

scope_demo.c 💡 Hover over any highlighted variable
#include <stdio.h> int count = 0; void incrementCounter() { count++; printf("Inside func: %d\n", count); } void shadowDemo() { int count = 99; printf("Shadow: %d\n", count); } int main() { int num = 5; incrementCounter(); incrementCounter(); shadowDemo(); printf("Global count: %d\n", count); return 0; }
Quick Reference

Scope Rules at a Glance

Scope rules prevent the most common bugs — accessing a destroyed variable, or accidentally modifying a global.

Variable TypeDeclared Where?Accessible InLifetimeDefault Value
Local VariableInside a function / block { }Only that blockCreated on call, destroyed on returnGarbage (undefined!)
Global VariableOutside all functionsEntire programWhole program execution0 (auto-initialized)
Formal ParameterFunction parameter listOnly that functionCreated on call, destroyed on returnCaller’s value (copy)
Block VariableInside if / for / whileOnly that blockBlock entry → block exitGarbage
⚠️

Avoid Globals When Possible

Any function can modify them — creates hidden side-effects and makes debugging hard.

💀

Local Vars Die on Return

Never return a pointer to a local variable — that memory is released after the function ends!

📚

See Also: Storage Classes

static locals survive between calls. 📖 Lecture 6 →

Functions Calling Functions

Nested Function Calls

C functions can call other functions. This creates a chain of calls — each with its own local scope. Control flows back like a stack of trays.

nested_calls.cFull Example
#include <stdio.h> // Level 3 — innermost int square(int x) { return x * x; // x is LOCAL to square } // Level 2 — calls square() int sumOfSquares(int a, int b) { return square(a) + square(b); } // Level 1 — calls sumOfSquares() int main() { int result = sumOfSquares(3, 4); printf("Result: %d\n", result); // 9+16=25 return 0; } // ⚠ C does NOT allow defining a function // INSIDE another function (unlike Python). // All definitions must be at file level!
Ready. Click to start tracing calls.
📚 Active Function Stack
Stack is empty
Putting It All Together

Complete Working Example

One program demonstrating: prototype, definition, calling, local scope, global variable, and nested calls — all together.

complete_example.cFull Program
#include <stdio.h> // ① Global variable — accessible everywhere int callCount = 0; // ② Prototypes int square(int x); int cube(int x); void printPowers(int n); // ③ Definitions int square(int x) { callCount++; int result = x * x; // LOCAL variable return result; } int cube(int x) { callCount++; return square(x) * x; // nested call! } void printPowers(int n) { printf("n=%d sq=%d cube=%d\n", n, square(n), cube(n)); } int main() { printPowers(3); printPowers(4); printf("Total calls: %d\n", callCount); return 0; }

🖥️ Expected Output

n=3 sq=9 cube=27
n=4 sq=16 cube=64
Total calls: 6

✅ Concepts Used

  • Global callCount — shared across all
  • Prototypes — all 3 declared before main
  • Local Scope — result in square() only
  • Nested Call — cube() calls square()
  • void Function — printPowers returns nothing
💡 Why callCount = 6? printPowers(3) calls square once + cube once (cube internally calls square). That’s 3 per call × 2 calls = 6 total.
Test Your Understanding

Quick Quiz 🎯

6 questions covering functions, scope, prototypes, and nested calls.

Question 1 of 6
Loading…