Introduction to Functions
Functions are the building blocks of C programs. They allow you to break down complex problems into smaller, manageable pieces of code that can be reused throughout your program. Think of functions as mini-programs within your main program, each designed to perform a specific task.
What is a Function?
Imagine you're cooking a complex meal. Instead of doing everything at once, you break it down into steps: chop vegetables, prepare sauce, cook rice, and so on. Each step is like a function - a self-contained task that you can repeat whenever needed. In programming, functions work the same way.
Function
A function is a self-contained block of code that performs a specific task. It takes input (called parameters), processes it, and optionally returns a result. Functions are defined once but can be called (executed) multiple times from different parts of your program.
Think of a function like a vending machine: you put something in (money/selection), the machine does its work (internal processing), and you get something out (your snack). You don't need to know how the machine works inside - you just use it.
Key characteristics: Reusable (write once, use many times), Modular (self-contained), Maintainable (easy to update in one place), Testable (can verify each function independently).
Why Do We Need Functions?
Without functions, you would have to write the same code over and over again every time you needed to perform a task. This leads to longer, harder-to-read programs that are prone to bugs. Functions solve these problems by allowing you to write code once and reuse it.
Code Reusability
Write code once and use it multiple times throughout your program without duplication.
Modularity
Break complex problems into smaller, manageable pieces that are easier to understand.
Easy Maintenance
Fix bugs or update logic in one place, and all calls to that function are automatically updated.
Easier Testing
Test each function independently to ensure it works correctly before integrating it.
Your First Function: A Simple Example
You've already been using a function without realizing it - the main() function!
Every C program must have a main() function because that's where program execution begins.
Now let's create our own custom function.
#include <stdio.h>
// A simple function that prints a greeting
void sayHello() {
printf("Hello, World!\n");
}
int main() {
sayHello(); // Call the function
sayHello(); // Call it again - reuse!
sayHello(); // And again!
return 0;
}
// Output:
// Hello, World!
// Hello, World!
// Hello, World!
In this example, we defined a function called sayHello() that prints a message.
We then called it three times from main(). Notice how we wrote the printing code
once but used it three times - that's the power of functions!
main()
function. It's the entry point where your program starts executing. The operating system calls
main() when you run your program.
Library Functions vs User-Defined Functions
C provides two types of functions: those that come built-in with the language (library functions) and those you create yourself (user-defined functions). Both work the same way, but they come from different sources.
| Aspect | Library Functions | User-Defined Functions |
|---|---|---|
| Source | Pre-written, included via header files | Written by you in your code |
| Examples | printf(), scanf(), sqrt() |
calculateArea(), validateInput() |
| Header Required | Yes (e.g., #include <stdio.h>) |
No (defined in your file) |
| Modification | Cannot be modified | Full control to modify |
#include <stdio.h> // For printf() - library function
#include <math.h> // For sqrt() - library function
// User-defined function to calculate circle area
double calculateCircleArea(double radius) {
return 3.14159 * radius * radius;
}
int main() {
double radius = 5.0;
// Using library functions
printf("Radius: %.2f\n", radius); // printf is a library function
printf("Square root: %.2f\n", sqrt(radius)); // sqrt is a library function
// Using our user-defined function
double area = calculateCircleArea(radius);
printf("Circle area: %.2f\n", area);
return 0;
}
// Output:
// Radius: 5.00
// Square root: 2.24
// Circle area: 78.54
This example demonstrates both types of functions working together. We use printf() and
sqrt() from the standard library, and our own calculateCircleArea() function.
The library functions are ready to use after including the header files, while our custom function
gives us complete control over the calculation logic.
Practice Questions: Introduction
Task: Write a function called printName() that prints your name. Call it from main().
Show Solution
#include <stdio.h>
void printName() {
printf("My name is Rahul\n");
}
int main() {
printName();
return 0;
}
// Output: My name is Rahul
Task: Write a function called printLine() that prints 20 dashes. Use it to create a formatted output.
Expected output:
--------------------
WELCOME
--------------------
Show Solution
#include <stdio.h>
void printLine() {
printf("--------------------\n");
}
int main() {
printLine();
printf(" WELCOME\n");
printLine();
return 0;
}
Task: Create three functions: sayGoodMorning(), sayGoodAfternoon(), and sayGoodNight(). Each should print an appropriate greeting. Call all three from main().
Show Solution
#include <stdio.h>
void sayGoodMorning() {
printf("Good Morning! Rise and shine!\n");
}
void sayGoodAfternoon() {
printf("Good Afternoon! Hope you're having a great day!\n");
}
void sayGoodNight() {
printf("Good Night! Sweet dreams!\n");
}
int main() {
sayGoodMorning();
sayGoodAfternoon();
sayGoodNight();
return 0;
}
Task: Write a function called displayMenu() that shows a calculator menu with 4 options. Call it twice from main().
Show Solution
#include <stdio.h>
void displayMenu() {
printf("\n=== Calculator Menu ===\n");
printf("1. Addition\n");
printf("2. Subtraction\n");
printf("3. Multiplication\n");
printf("4. Division\n");
printf("=======================\n");
}
int main() {
displayMenu();
printf("First menu displayed!\n");
displayMenu();
printf("Second menu displayed!\n");
return 0;
}
Task: Write a function called printBox() that prints a 5x5 box of asterisks. Use nested loops inside the function.
Expected output:
*****
*****
*****
*****
*****
Show Solution
#include <stdio.h>
void printBox() {
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
printf("*");
}
printf("\n");
}
}
int main() {
printBox();
return 0;
}
Function Declaration and Definition
Understanding the anatomy of a function is crucial. Every function in C has specific parts that work together: the return type, name, parameters, and body. Let's break down each component and understand the difference between declaring and defining a function.
Anatomy of a Function
A function definition consists of four main parts. Understanding each part helps you write functions correctly and understand what they do at a glance.
Function Syntax
return_type function_name(parameter_list) {
// Function body
// Statements to execute
return value; // Optional (required if return_type is not void)
}
| Component | Description | Example |
|---|---|---|
| Return Type | Data type of the value the function returns | int, double, void |
| Function Name | Identifier used to call the function | calculateSum, printMessage |
| Parameters | Input values the function receives | (int a, int b), (double radius) |
| Function Body | Code that executes when function is called | { ... } block with statements |
// Example with all parts labeled
// | | | |
// v v v v
// return function parameter parameter
// type name 1 2
int addNumbers (int firstNum, int secondNum) {
// Function body starts here
int sum = firstNum + secondNum;
return sum; // Return statement
}
// Function body ends here
This visual breakdown shows how addNumbers is constructed: int specifies it returns
an integer, the name follows C naming rules, two int parameters accept input values, and the
body calculates and returns their sum. Each part has a specific purpose in making the function work.
Function Definition
A function definition is the complete implementation of a function. It includes the function header (return type, name, parameters) AND the function body (the actual code that runs when the function is called). This is where you write the logic of what the function does.
Think of the definition as the "recipe" - it contains all the instructions. When you call the function, C follows these instructions to produce the result.
Remember: You define a function once, but you can call it as many times as needed.
Function Naming Conventions
Good function names make your code readable and self-documenting. A well-named function tells you what it does without needing to read its implementation. Follow these conventions:
- Use camelCase or snake_case - Be consistent throughout your project
- Start with a verb - Functions DO things:
calculate,print,get,set - Be descriptive -
calculateTax()is better thancalc() - Avoid abbreviations -
getMaximumValue()notgetMaxVal()
// Good function names - clear and descriptive
int calculateSum(int a, int b);
void printStudentDetails(char name[], int age);
double getCircleArea(double radius);
int isValidInput(int value);
// Bad function names - unclear and confusing
int cs(int a, int b); // What does cs mean?
void psd(char n[], int a); // Abbreviations are confusing
double gca(double r); // Hard to understand
int check(int v); // Check what?
The good examples clearly communicate their purpose: calculateSum adds numbers,
printStudentDetails displays student info, isValidInput checks validity.
The bad examples use cryptic abbreviations that force readers to look at the implementation to
understand what the function does - defeating the purpose of good naming.
int, return, or if.
Complete Function Examples
Let's look at several complete function examples to solidify your understanding. Each example shows a different use case for functions.
Example 1: Function with No Parameters and No Return
#include <stdio.h>
// Function definition
void printWelcome() {
printf("================================\n");
printf(" Welcome to My Program!\n");
printf("================================\n");
}
int main() {
printWelcome(); // Function call
printf("Let's get started...\n");
return 0;
}
// Output:
// ================================
// Welcome to My Program!
// ================================
// Let's get started...
The printWelcome() function takes no input and returns nothing (void).
It simply performs an action - printing a formatted welcome banner. This is perfect for
repetitive display tasks. When called from main(), the program jumps to execute
the function, then continues with the next line.
Example 2: Function with Parameters and Return Value
#include <stdio.h>
// Function to calculate rectangle area
int calculateRectangleArea(int length, int width) {
int area = length * width;
return area;
}
int main() {
int len = 10;
int wid = 5;
int result = calculateRectangleArea(len, wid);
printf("Rectangle area: %d square units\n", result);
// Can also use directly in printf
printf("Another rectangle: %d\n", calculateRectangleArea(8, 3));
return 0;
}
// Output:
// Rectangle area: 50 square units
// Another rectangle: 24
This function accepts two integer parameters (length and width) and returns
their product as the area. Notice how the returned value can be stored in a variable (result)
or used directly inside printf(). The function encapsulates the area calculation logic,
making it reusable for any rectangle dimensions.
Example 3: Function with Multiple Parameters
#include <stdio.h>
// Function to find the maximum of three numbers
int findMaximum(int a, int b, int c) {
int max = a;
if (b > max) {
max = b;
}
if (c > max) {
max = c;
}
return max;
}
int main() {
int num1 = 25, num2 = 42, num3 = 18;
int maximum = findMaximum(num1, num2, num3);
printf("Maximum of %d, %d, %d is: %d\n", num1, num2, num3, maximum);
return 0;
}
// Output:
// Maximum of 25, 42, 18 is: 42
The findMaximum() function demonstrates handling multiple parameters and using
conditional logic inside a function. It compares three numbers and returns the largest one.
The function isolates the comparison logic, so you can find the maximum of any three numbers
by simply calling findMaximum(a, b, c) without rewriting the comparison code.
Practice Questions: Declaration & Definition
Task: Create a function square() that takes an integer and returns its square. Test it with the number 7.
Show Solution
#include <stdio.h>
int square(int num) {
return num * num;
}
int main() {
int result = square(7);
printf("Square of 7 is: %d\n", result); // 49
return 0;
}
Task: Create a function addFloats() that takes two double values and returns their sum.
Show Solution
#include <stdio.h>
double addFloats(double a, double b) {
return a + b;
}
int main() {
double sum = addFloats(3.14, 2.86);
printf("Sum: %.2f\n", sum); // 6.00
return 0;
}
Task: Create a function isEven() that returns 1 if the number is even, 0 if odd.
Show Solution
#include <stdio.h>
int isEven(int num) {
if (num % 2 == 0) {
return 1; // True - is even
}
return 0; // False - is odd
}
// Or shorter version:
// int isEven(int num) { return num % 2 == 0; }
int main() {
printf("Is 4 even? %d\n", isEven(4)); // 1
printf("Is 7 even? %d\n", isEven(7)); // 0
return 0;
}
Task: Create calculateAverage() that takes three doubles and returns their average.
Show Solution
#include <stdio.h>
double calculateAverage(double a, double b, double c) {
return (a + b + c) / 3.0;
}
int main() {
double avg = calculateAverage(85.5, 90.0, 78.5);
printf("Average: %.2f\n", avg); // 84.67
return 0;
}
Task: Create power() that calculates base raised to exponent using a loop (don't use pow() from math.h).
Show Solution
#include <stdio.h>
int power(int base, int exponent) {
int result = 1;
for (int i = 0; i < exponent; i++) {
result = result * base;
}
return result;
}
int main() {
printf("2^5 = %d\n", power(2, 5)); // 32
printf("3^4 = %d\n", power(3, 4)); // 81
printf("5^0 = %d\n", power(5, 0)); // 1
return 0;
}
Return Types and the Return Statement
The return type specifies what kind of value a function sends back to the code that called it. Understanding return types is essential because it determines how you can use the function's result in your program.
The void Return Type
When a function doesn't need to return any value, we use the void keyword. These functions
perform an action (like printing) but don't produce a result that needs to be stored or used elsewhere.
Think of them as "do something" functions rather than "calculate something" functions.
void
The void return type indicates that a function does not return any value. These functions are used for their side effects (printing, modifying global variables, etc.) rather than for computing a value.
A void function can use return; (without a value) to exit early, but it's not
required at the end of the function.
Use void when: The function's purpose is to perform an action, not calculate a result.
#include <stdio.h>
// void function - doesn't return anything
void printGreeting(char name[]) {
printf("Hello, %s! Welcome to C programming.\n", name);
// No return statement needed (or use 'return;' to exit early)
}
void printStars(int count) {
for (int i = 0; i < count; i++) {
printf("*");
}
printf("\n");
}
int main() {
printGreeting("Priya");
printStars(10);
// These would cause errors:
// int result = printGreeting("Test"); // Error! void has no return value
// printf("%d", printStars(5)); // Error! void cannot be printed
return 0;
}
// Output:
// Hello, Priya! Welcome to C programming.
// **********
Both functions are void - they perform actions but don't return values.
printGreeting() takes a string parameter to personalize the message, while
printStars() uses a loop to print a variable number of stars. The commented-out
lines show what happens if you try to use a void function's "return value" - it causes errors
because there's nothing to store or print.
Returning Values
When a function calculates something, it needs to send that result back to the calling code.
The return statement does this. The value returned must match the declared return type,
or the compiler will either give an error or perform an automatic (potentially lossy) conversion.
Returning an Integer
// Returns an integer
int add(int a, int b) {
return a + b; // Returns the sum
}
The add() function takes two integers, calculates their sum, and returns the result
as an int. The return statement sends the value of a + b
back to wherever the function was called.
Returning a Double
// Returns a double (decimal number)
double divide(double numerator, double denominator) {
return numerator / denominator;
}
The divide() function returns a double to preserve decimal precision.
If we used int as the return type, dividing 10 by 3 would give 3 instead of 3.33.
Always choose the return type that matches the kind of result you need.
Returning a Character
// Returns a character
char getGrade(int score) {
if (score >= 90) return 'A';
if (score >= 80) return 'B';
if (score >= 70) return 'C';
if (score >= 60) return 'D';
return 'F';
}
The getGrade() function returns a char representing the letter grade.
It uses multiple return statements - once any return executes, the
function immediately exits. This pattern eliminates the need for else statements.
Using the Returned Values
int main() {
// Store returned value in a variable
int sum = add(5, 3);
printf("Sum: %d\n", sum); // 8
// Store double result
double result = divide(10.0, 3.0);
printf("Division: %.2f\n", result); // 3.33
// Store character result
char grade = getGrade(85);
printf("Grade: %c\n", grade); // B
return 0;
}
Each function call returns a value that gets stored in a variable of the matching type.
The variable sum holds the integer result from add(),
result holds the double from divide(), and grade
holds the character from getGrade(). You can then use these variables in
printf() or other calculations.
Multiple Return Statements
A function can have multiple return statements. When any return is executed,
the function immediately stops and returns that value. This is useful for handling different cases
or exiting early when a condition is met.
Absolute Value Function
int absoluteValue(int num) {
if (num < 0) {
return -num; // Return positive version
}
return num; // Already positive
}
The absoluteValue() function has two return paths. If the number is negative,
it returns -num (which makes it positive) and exits immediately. If the number
is already positive or zero, it skips the if block and returns the original number. No
else is needed because return exits the function.
Find Minimum of Two Numbers
int minimum(int a, int b) {
if (a < b) {
return a;
}
return b;
}
The minimum() function compares two numbers and returns the smaller one.
If a is less than b, it returns a immediately.
Otherwise, b must be smaller (or equal), so it returns b.
This is a clean pattern for comparison functions.
Validation Function
int isValidAge(int age) {
if (age < 0) {
return 0; // Invalid: negative
}
if (age > 150) {
return 0; // Invalid: too old
}
return 1; // Valid age
}
The isValidAge() function checks multiple conditions and returns early if any
fail. It returns 0 (false) for invalid ages (negative or over 150) and only
returns 1 (true) if all checks pass. This "guard clause" pattern makes validation
logic easy to read and extend.
Testing the Functions
int main() {
printf("Absolute of -5: %d\n", absoluteValue(-5)); // 5
printf("Absolute of 3: %d\n", absoluteValue(3)); // 3
printf("Minimum of 8 and 3: %d\n", minimum(8, 3)); // 3
printf("Is 25 valid age? %d\n", isValidAge(25)); // 1
printf("Is -5 valid age? %d\n", isValidAge(-5)); // 0
return 0;
}
The output shows each function working correctly: absoluteValue(-5) returns 5,
minimum(8, 3) returns 3, and isValidAge() returns 1 for valid
ages and 0 for invalid ones. Each function handles its specific case using multiple returns.
return statement executes, the function ends immediately.
Any code after the return statement will not run. This is why you don't need else after
returning - the function has already exited!
Common Return Type Mistakes
Here are some common errors beginners make with return types:
// MISTAKE 1: Returning wrong type
int getAverage(int a, int b) {
return (a + b) / 2.0; // Returns double, but function returns int!
// The decimal part will be lost (truncated)
}
// CORRECT version:
double getAverageCorrect(int a, int b) {
return (a + b) / 2.0; // Now it can return decimal
}
// MISTAKE 2: Forgetting to return a value
int getPositive(int num) {
if (num > 0) {
return num;
}
// What if num <= 0? No return! This is undefined behavior.
}
// CORRECT version:
int getPositiveCorrect(int num) {
if (num > 0) {
return num;
}
return 0; // Always return something!
}
// MISTAKE 3: Trying to return a value from void function
void printAndReturn(int num) {
printf("%d\n", num);
return num; // ERROR! void functions can't return values
}
// CORRECT version (if you need to return):
int printAndReturnCorrect(int num) {
printf("%d\n", num);
return num; // Now it's allowed
}
These examples highlight common pitfalls: Mistake 1 shows type mismatch where a double result gets truncated when returned as int. Mistake 2 demonstrates missing return paths - if the condition is false, no value is returned. Mistake 3 shows attempting to return a value from a void function. Each correct version fixes the issue by using appropriate return types and ensuring all code paths return a value.
Practice Questions: Return Types
Task: Create getMax() that returns the larger of two integers.
Show Solution
#include <stdio.h>
int getMax(int a, int b) {
if (a > b) {
return a;
}
return b;
}
int main() {
printf("Max of 10 and 25: %d\n", getMax(10, 25)); // 25
printf("Max of 50 and 30: %d\n", getMax(50, 30)); // 50
return 0;
}
Task: Create doubleIt() that returns a number multiplied by 2.
Show Solution
#include <stdio.h>
int doubleIt(int num) {
return num * 2;
}
int main() {
printf("Double of 7: %d\n", doubleIt(7)); // 14
printf("Double of 15: %d\n", doubleIt(15)); // 30
return 0;
}
Task: Create celsiusToFahrenheit() using formula: F = (C * 9/5) + 32
Show Solution
#include <stdio.h>
double celsiusToFahrenheit(double celsius) {
return (celsius * 9.0 / 5.0) + 32.0;
}
int main() {
printf("0C = %.1fF\n", celsiusToFahrenheit(0)); // 32.0
printf("100C = %.1fF\n", celsiusToFahrenheit(100)); // 212.0
printf("37C = %.1fF\n", celsiusToFahrenheit(37)); // 98.6
return 0;
}
Task: Create isPositive() that returns 1 for positive, 0 for zero, -1 for negative.
Show Solution
#include <stdio.h>
int isPositive(int num) {
if (num > 0) {
return 1; // Positive
} else if (num == 0) {
return 0; // Zero
} else {
return -1; // Negative
}
}
int main() {
printf("5: %d\n", isPositive(5)); // 1
printf("0: %d\n", isPositive(0)); // 0
printf("-3: %d\n", isPositive(-3)); // -1
return 0;
}
Task: Create factorial() that returns n! (n factorial). Use a loop. Handle edge case of 0! = 1.
Show Solution
#include <stdio.h>
long factorial(int n) {
if (n < 0) {
return -1; // Invalid input
}
long result = 1;
for (int i = 2; i <= n; i++) {
result = result * i;
}
return result;
}
int main() {
printf("0! = %ld\n", factorial(0)); // 1
printf("5! = %ld\n", factorial(5)); // 120
printf("10! = %ld\n", factorial(10)); // 3628800
return 0;
}
Function Prototypes
A function prototype tells the compiler about a function's existence before its actual definition. This is essential in C because the compiler reads your code from top to bottom - it needs to know about functions before they're called.
What is a Function Prototype?
When you call a function, the compiler needs to know three things: the function's name, what parameters it takes, and what type of value it returns. A prototype provides this information without the actual function body - it's like a "promise" that the function will be defined somewhere.
Function Prototype (Declaration)
A function prototype (also called a function declaration) declares the function's interface without providing the implementation. It specifies the return type, function name, and parameter types, ending with a semicolon.
Think of a prototype like a restaurant menu - it tells you what's available (function name), what you need to provide (parameters), and what you'll get back (return type), but it doesn't show you how the food is prepared (function body).
Syntax: return_type function_name(parameter_types);
// Function prototypes (declarations) - just the "signature" with semicolon
int add(int a, int b);
void printMessage(char message[]);
double calculateArea(double radius);
// Note: Parameter names are optional in prototypes
int multiply(int, int); // Also valid
Prototypes declare the function's interface without the body, ending with a semicolon. They tell the compiler: "This function exists, takes these parameters, and returns this type." Parameter names are optional in prototypes (only types matter), but including them improves readability. The actual implementation comes later in the function definition.
Why Do We Need Prototypes?
C compiles your code from top to bottom. Without prototypes, if you call a function before its definition, the compiler doesn't know what parameters it should accept or what value it returns. This causes errors or, worse, undefined behavior.
The Problem Without Prototypes
// This will cause a problem!
#include <stdio.h>
int main() {
int result = add(5, 3); // ERROR! Compiler doesn't know add() yet
printf("Result: %d\n", result);
return 0;
}
// Function defined AFTER main() - compiler hasn't seen it
int add(int a, int b) {
return a + b;
}
When the compiler reaches add(5, 3) in main(), it hasn't yet seen
the add() function definition below. The compiler doesn't know what parameters
add() expects or what type it returns, causing a compilation error. This is the
core problem that prototypes solve.
Solution 1: Define Before Use (Not Always Practical)
#include <stdio.h>
// Define the function BEFORE it's used
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3); // Works! Compiler has already seen add()
printf("Result: %d\n", result);
return 0;
}
By placing the add() function definition before main(), the compiler
knows about it when the call occurs. However, this approach becomes impractical with many functions,
especially when functions call each other (mutual recursion) or when you want main()
at the top for readability.
Solution 2: Use a Prototype (Recommended)
#include <stdio.h>
// Function prototype - tells compiler about add() before it's defined
int add(int a, int b);
int main() {
int result = add(5, 3); // Works! Compiler knows add() exists
printf("Result: %d\n", result);
return 0;
}
// Full definition can come later
int add(int a, int b) {
return a + b;
}
The prototype at the top tells the compiler: "Trust me, add() exists, takes two ints,
and returns an int." Now when the compiler sees add(5, 3) in main(),
it can verify the call is correct. The actual function definition can appear anywhere later in the file.
This is the recommended approach for organizing C programs.
#include statements). This makes your code organized and ensures all functions
can be called from anywhere in the file.
Complete Example: Organized Code with Prototypes
Here's a well-organized C program that uses prototypes. Notice how the structure makes it easy to see what functions are available, and the main() function comes first (making the program's entry point clear).
#include <stdio.h>
// ============================================
// Function Prototypes (declarations)
// ============================================
void displayMenu();
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
double divide(double a, double b);
// ============================================
// Main Function
// ============================================
int main() {
displayMenu();
int x = 20, y = 5;
printf("\nCalculations with %d and %d:\n", x, y);
printf("Add: %d\n", add(x, y));
printf("Subtract: %d\n", subtract(x, y));
printf("Multiply: %d\n", multiply(x, y));
printf("Divide: %.2f\n", divide(x, y));
return 0;
}
// ============================================
// Function Definitions
// ============================================
void displayMenu() {
printf("=== Simple Calculator ===\n");
printf("Operations: +, -, *, /\n");
}
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
double divide(double a, double b) {
if (b == 0) {
printf("Error: Division by zero!\n");
return 0;
}
return a / b;
}
// Output:
// === Simple Calculator ===
// Operations: +, -, *, /
//
// Calculations with 20 and 5:
// Add: 25
// Subtract: 15
// Multiply: 100
// Divide: 4.00
This well-structured program demonstrates best practices: prototypes at the top declare all functions,
main() follows showing the program's flow at a glance, and definitions come last with
the implementation details. The divide() function also shows defensive programming by
checking for division by zero before performing the operation.
- Prototype:
int add(int a, int b);- Ends with semicolon, no body - Definition:
int add(int a, int b) { return a + b; }- Has the body with curly braces
Practice Questions: Prototypes
Task: Write just the prototype (not the definition) for a function sayHello that takes no parameters and returns nothing.
Show Solution
void sayHello();
Task: Write the prototype for getSum that takes two integers and returns their sum.
Show Solution
int getSum(int a, int b);
// Or without parameter names:
int getSum(int, int);
Task: This code has errors. Add prototypes to fix it:
#include <stdio.h>
int main() {
printResult(add(3, 4));
return 0;
}
int add(int a, int b) {
return a + b;
}
void printResult(int num) {
printf("Result: %d\n", num);
}
Show Solution
#include <stdio.h>
// Add prototypes at the top
int add(int a, int b);
void printResult(int num);
int main() {
printResult(add(3, 4));
return 0;
}
int add(int a, int b) {
return a + b;
}
void printResult(int num) {
printf("Result: %d\n", num);
}
Task: Write prototypes for: circleArea(radius), rectangleArea(l, w), triangleArea(base, height). All take and return doubles.
Show Solution
double circleArea(double radius);
double rectangleArea(double length, double width);
double triangleArea(double base, double height);
Task: Create a program with: displayHeader(), getSquare(int), getCube(int). Use proper prototypes, put main() after prototypes but before definitions.
Show Solution
#include <stdio.h>
// Prototypes
void displayHeader();
int getSquare(int num);
int getCube(int num);
// Main
int main() {
displayHeader();
int n = 5;
printf("%d squared = %d\n", n, getSquare(n));
printf("%d cubed = %d\n", n, getCube(n));
return 0;
}
// Definitions
void displayHeader() {
printf("=== Power Calculator ===\n");
}
int getSquare(int num) {
return num * num;
}
int getCube(int num) {
return num * num * num;
}
Calling Functions and Program Flow
Understanding how function calls work is key to mastering C programming. When you call a function, program execution jumps to that function, runs its code, and then returns to where it was called. Let's visualize this flow and explore different ways to use function return values.
How Function Calls Work
When you call a function, several things happen behind the scenes. Think of it like making a phone call - you pause what you're doing, have a conversation, and then resume where you left off.
Save Current Position
The program remembers where it was (which line it was executing) so it can return later.
Pass Arguments
The values you provide in the function call are copied to the function's parameters.
Execute Function Body
The code inside the function runs from top to bottom until a return statement or the end.
Return Value (if any)
The function sends back a value to the calling code (or nothing if void).
Resume Execution
The program continues from where it left off, using the returned value if needed.
#include <stdio.h>
int double_it(int num) {
printf(" Inside function: num = %d\n", num);
return num * 2;
}
int main() {
printf("1. Starting main()\n");
printf("2. About to call double_it(5)\n");
int result = double_it(5); // Execution jumps to double_it
printf("3. Back in main, result = %d\n", result);
printf("4. Program ends\n");
return 0;
}
// Output:
// 1. Starting main()
// 2. About to call double_it(5)
// Inside function: num = 5
// 3. Back in main, result = 10
// 4. Program ends
The output clearly shows the execution flow: main() runs until it hits
double_it(5), then execution jumps into the function (printing "Inside function"),
the function returns 10, and execution resumes in main() with the
result stored. This "pause, jump, return, resume" pattern is fundamental to understanding
how functions work.
Different Ways to Use Return Values
When a function returns a value, you have several options for what to do with it:
#include <stdio.h>
int square(int n) {
return n * n;
}
int main() {
// Method 1: Store in a variable
int result = square(5);
printf("Stored: %d\n", result); // 25
// Method 2: Use directly in printf
printf("Direct: %d\n", square(6)); // 36
// Method 3: Use in an expression
int sum = square(3) + square(4);
printf("Sum of squares: %d\n", sum); // 9 + 16 = 25
// Method 4: Use in a condition
if (square(5) > 20) {
printf("25 is greater than 20\n");
}
// Method 5: Pass as argument to another function
printf("Square of square: %d\n", square(square(2))); // square(4) = 16
// Method 6: Ignore the return value (valid but usually not recommended)
square(10); // Result is calculated but not used
return 0;
}
This example shows six different ways to use a function's return value: store it in a variable
for later use, use it directly in printf(), combine multiple calls in an expression,
test it in a condition, pass it as an argument to another function call, or ignore it entirely.
Choosing the right approach depends on whether you need the value once, multiple times, or not at all.
Nested Function Calls
You can call functions within functions, passing the return value of one as an argument to another. This is called nested or chained function calls. The innermost function executes first.
#include <stdio.h>
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
int square(int n) { return n * n; }
int main() {
// Nested calls - innermost executes first
int result = add(multiply(2, 3), square(4));
// Step 1: multiply(2, 3) = 6
// Step 2: square(4) = 16
// Step 3: add(6, 16) = 22
printf("Result: %d\n", result); // 22
// More complex nesting
printf("%d\n", square(add(2, 3))); // square(5) = 25
printf("%d\n", multiply(add(1, 2), add(3, 4))); // 3 * 7 = 21
return 0;
}
Nested calls evaluate from inside out. In add(multiply(2,3), square(4)), first
multiply(2,3) returns 6, then square(4) returns 16, finally
add(6, 16) returns 22. The last example multiply(add(1,2), add(3,4))
evaluates both add() calls (returning 3 and 7), then multiplies them to get 21.
Real-World Example: Simple Calculator
Let's put everything together in a practical example - a simple calculator that demonstrates function prototypes, definitions, and calls working together.
#include <stdio.h>
// Function prototypes
void displayWelcome();
void displayMenu();
int getChoice();
double calculate(double a, double b, int operation);
void displayResult(double result);
int main() {
displayWelcome();
// Get two numbers
double num1, num2;
printf("Enter first number: ");
scanf("%lf", &num1);
printf("Enter second number: ");
scanf("%lf", &num2);
displayMenu();
int choice = getChoice();
double result = calculate(num1, num2, choice);
displayResult(result);
return 0;
}
// Function definitions
void displayWelcome() {
printf("\n=================================\n");
printf(" Welcome to Simple Calculator\n");
printf("=================================\n\n");
}
void displayMenu() {
printf("\nOperations:\n");
printf("1. Add\n");
printf("2. Subtract\n");
printf("3. Multiply\n");
printf("4. Divide\n");
}
int getChoice() {
int choice;
printf("Enter your choice (1-4): ");
scanf("%d", &choice);
return choice;
}
double calculate(double a, double b, int operation) {
switch (operation) {
case 1: return a + b;
case 2: return a - b;
case 3: return a * b;
case 4:
if (b != 0) return a / b;
printf("Error: Division by zero!\n");
return 0;
default:
printf("Invalid operation!\n");
return 0;
}
}
void displayResult(double result) {
printf("\n---------------------------------\n");
printf("Result: %.2f\n", result);
printf("---------------------------------\n");
}
This calculator demonstrates real-world function organization: displayWelcome() and
displayMenu() handle user interface, getChoice() handles input,
calculate() contains the core logic with a switch statement, and displayResult()
formats the output. Each function has a single responsibility, making the code modular, testable,
and easy to modify.
Practice Questions: Calling Functions
Task: What will this print?
int double_it(int n) { return n * 2; }
int add_one(int n) { return n + 1; }
printf("%d", double_it(add_one(4)));
Show Solution
// Step 1: add_one(4) = 5
// Step 2: double_it(5) = 10
// Output: 10
Task: Create a function printStar() and call it 5 times using a loop to print 5 stars on one line.
Show Solution
#include <stdio.h>
void printStar() {
printf("*");
}
int main() {
for (int i = 0; i < 5; i++) {
printStar();
}
printf("\n"); // *****
return 0;
}
Task: Create isAdult(int age) that returns 1 if age >= 18, else 0. Use it in an if statement.
Show Solution
#include <stdio.h>
int isAdult(int age) {
return age >= 18;
}
int main() {
int age = 16;
if (isAdult(age)) {
printf("You can vote!\n");
} else {
printf("You cannot vote yet.\n");
}
return 0;
}
Task: Create triple(int) and addTen(int). Calculate triple(addTen(5)) and addTen(triple(5)).
Show Solution
#include <stdio.h>
int triple(int n) { return n * 3; }
int addTen(int n) { return n + 10; }
int main() {
printf("triple(addTen(5)) = %d\n", triple(addTen(5))); // triple(15) = 45
printf("addTen(triple(5)) = %d\n", addTen(triple(5))); // addTen(15) = 25
return 0;
}
Task: Create: getAverage(int s1, int s2, int s3), getGrade(double avg), printReport(int s1, int s2, int s3). printReport should use the other two functions.
Show Solution
#include <stdio.h>
double getAverage(int s1, int s2, int s3) {
return (s1 + s2 + s3) / 3.0;
}
char getGrade(double avg) {
if (avg >= 90) return 'A';
if (avg >= 80) return 'B';
if (avg >= 70) return 'C';
if (avg >= 60) return 'D';
return 'F';
}
void printReport(int s1, int s2, int s3) {
double avg = getAverage(s1, s2, s3);
char grade = getGrade(avg);
printf("Scores: %d, %d, %d\n", s1, s2, s3);
printf("Average: %.2f\n", avg);
printf("Grade: %c\n", grade);
}
int main() {
printReport(85, 90, 78);
return 0;
}
Key Takeaways
Functions are Building Blocks
Break complex programs into smaller, reusable pieces that each do one specific task well
Prototype Before Use
Declare functions with prototypes at the top so the compiler knows about them before they are called
Return Types Matter
Use void for no return, and matching types (int, double, char) when returning values
Name Functions Clearly
Use descriptive verb-based names like calculateSum, printMessage, isValidInput
Understand Program Flow
When a function is called, execution jumps there, runs the code, then returns to the caller
Reuse is the Goal
Write a function once, call it many times - this reduces code duplication and bugs
Knowledge Check
Quick Quiz
Test what you've learned about C functions