Functions in C Programming

Lecture 13 | Unit III: Array and Function
C Programming - BTech First Semester
Dr. Mohsin Dar | SOCS | UPES

Learning Objectives

  • Understand the concept and importance of functions in C
  • Learn function declaration, definition, and calling
  • Master parameter passing techniques
  • Understand return values and return types
  • Explore scope and lifetime of variables
  • Learn about recursive functions
  • Apply functions with arrays and strings

1. Introduction to Functions

What is a Function?

A function is a self-contained block of code that performs a specific task. Functions are the building blocks of C programs that help in:

  • Code Reusability: Write once, use multiple times
  • Modularity: Break complex problems into smaller parts
  • Maintainability: Easier to debug and modify
  • Readability: Makes code more organized and understandable
Key Point: Functions help us implement the "Divide and Conquer" approach in programming, where we solve a large problem by breaking it down into smaller, manageable sub-problems.

Types of Functions

Type Description Examples
Library Functions Pre-defined functions provided by C printf(), scanf(), strlen(), strcpy()
User-defined Functions Functions created by the programmer add(), factorial(), display()

2. Function Syntax and Structure

General Function Syntax:

return_type function_name(parameter_list) { // Function body // Local declarations // Executable statements return value; // optional }

Components of a Function

  • Return Type: Data type of the value returned (int, float, char, void)
  • Function Name: Identifier used to call the function
  • Parameter List: Input values (optional)
  • Function Body: Code that performs the task
  • Return Statement: Returns a value to the caller (optional for void)

Simple Function Example:

#include <stdio.h> // Function declaration (prototype) int add(int a, int b); int main() { int result = add(5, 3); printf("Sum = %d\n", result); return 0; } // Function definition int add(int a, int b) { return a + b; }
Output: Sum = 8

3. Parameters and Arguments

Function Categories Based on Parameters and Return Values

Category Parameters Return Value Example
No parameters, no return None void void display()
No parameters, with return None Any type int getNumber()
With parameters, no return Yes void void print(int x)
With parameters, with return Yes Any type int multiply(int a, int b)

Examples of Different Function Types:

#include <stdio.h> // 1. No parameters, no return value void greet() { printf("Hello, World!\n"); } // 2. No parameters, with return value int getCurrentYear() { return 2024; } // 3. With parameters, no return value void displaySquare(int num) { printf("Square of %d is %d\n", num, num * num); } // 4. With parameters, with return value float calculateArea(float radius) { return 3.14159 * radius * radius; } int main() { greet(); // Call function 1 int year = getCurrentYear(); // Call function 2 displaySquare(5); // Call function 3 float area = calculateArea(2.5); // Call function 4 printf("Current year: %d\n", year); printf("Area of circle: %.2f\n", area); return 0; }
Output:
Hello, World!
Square of 5 is 25
Current year: 2024
Area of circle: 19.63

Parameter Passing Methods

1. Call by Value (Pass by Value)

In this method, the actual values of arguments are copied to function parameters. Changes made to parameters inside the function do not affect the original variables.

#include <stdio.h> void modifyValue(int x) { x = x + 10; printf("Inside function: x = %d\n", x); } int main() { int num = 5; printf("Before function call: num = %d\n", num); modifyValue(num); printf("After function call: num = %d\n", num); return 0; }
Output:
Before function call: num = 5
Inside function: x = 15
After function call: num = 5

2. Call by Reference (Pass by Reference)

In this method, the address of variables is passed to function parameters using pointers. Changes made inside the function affect the original variables.

#include <stdio.h> void modifyValue(int *x) { *x = *x + 10; printf("Inside function: *x = %d\n", *x); } int main() { int num = 5; printf("Before function call: num = %d\n", num); modifyValue(&num); printf("After function call: num = %d\n", num); return 0; }
Output:
Before function call: num = 5
Inside function: *x = 15
After function call: num = 15

4. Functions with Arrays and Strings

Functions with Arrays

When passing arrays to functions, the array name acts as a pointer to the first element. The size of the array is usually passed as a separate parameter.

Array Processing Functions:

#include <stdio.h> // Function to display array elements void displayArray(int arr[], int size) { printf("Array elements: "); for(int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); } // Function to find sum of array elements int arraySum(int arr[], int size) { int sum = 0; for(int i = 0; i < size; i++) { sum += arr[i]; } return sum; } // Function to find maximum element int findMax(int arr[], int size) { int max = arr[0]; for(int i = 1; i < size; i++) { if(arr[i] > max) { max = arr[i]; } } return max; } int main() { int numbers[] = {10, 25, 3, 48, 17, 9, 33}; int size = sizeof(numbers) / sizeof(numbers[0]); displayArray(numbers, size); int total = arraySum(numbers, size); int maximum = findMax(numbers, size); printf("Sum of elements: %d\n", total); printf("Maximum element: %d\n", maximum); return 0; }
Output:
Array elements: 10 25 3 48 17 9 33
Sum of elements: 145
Maximum element: 48

Functions with Strings

String Processing Functions:

#include <stdio.h> // Function to calculate string length int stringLength(char str[]) { int length = 0; while(str[length] != '\0') { length++; } return length; } // Function to reverse a string void reverseString(char str[]) { int length = stringLength(str); for(int i = 0; i < length/2; i++) { char temp = str[i]; str[i] = str[length-1-i]; str[length-1-i] = temp; } } // Function to count vowels int countVowels(char str[]) { int count = 0; for(int i = 0; str[i] != '\0'; i++) { if(str[i] == 'a' || str[i] == 'e' || str[i] == 'i' || str[i] == 'o' || str[i] == 'u' || str[i] == 'A' || str[i] == 'E' || str[i] == 'I' || str[i] == 'O' || str[i] == 'U') { count++; } } return count; } int main() { char text[] = "Programming"; printf("Original string: %s\n", text); printf("Length: %d\n", stringLength(text)); printf("Vowel count: %d\n", countVowels(text)); reverseString(text); printf("Reversed string: %s\n", text); return 0; }
Output:
Original string: Programming
Length: 11
Vowel count: 3
Reversed string: gnimmargorP

5. Recursive Functions

What is Recursion?

Recursion is a programming technique where a function calls itself to solve a problem. Every recursive function must have:

  • Base Case: A condition that stops the recursion
  • Recursive Case: The function calling itself with modified parameters
Important: Without a proper base case, recursive functions will run infinitely, causing a stack overflow error.

Factorial Calculation using Recursion:

#include <stdio.h> long long factorial(int n) { // Base case if(n == 0 || n == 1) { return 1; } // Recursive case else { return n * factorial(n - 1); } } int main() { int num = 5; printf("%d! = %lld\n", num, factorial(num)); return 0; }
Output: 5! = 120

Fibonacci Series using Recursion:

#include <stdio.h> int fibonacci(int n) { // Base cases if(n == 0) return 0; if(n == 1) return 1; // Recursive case return fibonacci(n-1) + fibonacci(n-2); } void printFibonacci(int terms) { printf("Fibonacci Series: "); for(int i = 0; i < terms; i++) { printf("%d ", fibonacci(i)); } printf("\n"); } int main() { printFibonacci(10); return 0; }
Output: Fibonacci Series: 0 1 1 2 3 5 8 13 21 34

Recursion vs Iteration

Aspect Recursion Iteration
Memory Usage Higher (function call stack) Lower (variables only)
Speed Slower (function call overhead) Faster
Code Readability More readable for some problems Less readable for complex problems
Stack Overflow Risk Yes (for deep recursion) No

6. Scope and Lifetime of Variables

Variable Scope

Scope determines where in the program a variable can be accessed.

Scope Type Description Lifetime
Local Variables Declared inside functions Function execution time
Global Variables Declared outside all functions Entire program execution
Formal Parameters Function parameters Function execution time

Variable Scope Example:

#include <stdio.h> int globalVar = 100; // Global variable void function1(int param) { // param is formal parameter int localVar = 10; // Local variable printf("Inside function1:\n"); printf("Global variable: %d\n", globalVar); printf("Local variable: %d\n", localVar); printf("Parameter: %d\n", param); globalVar = 200; // Modifying global variable } void function2() { int localVar = 20; // Different local variable printf("Inside function2:\n"); printf("Global variable: %d\n", globalVar); printf("Local variable: %d\n", localVar); } int main() { int localVar = 30; // Local to main printf("In main function:\n"); printf("Global variable: %d\n", globalVar); printf("Local variable: %d\n", localVar); function1(50); function2(); printf("Back in main:\n"); printf("Global variable: %d\n", globalVar); return 0; }
Output:
In main function:
Global variable: 100
Local variable: 30
Inside function1:
Global variable: 100
Local variable: 10
Parameter: 50
Inside function2:
Global variable: 200
Local variable: 20
Back in main:
Global variable: 200

Storage Classes

Storage Class Keyword Scope Lifetime Initial Value
Automatic auto Local Function execution Garbage value
Register register Local Function execution Garbage value
Static static Local/Global Program execution 0 or NULL
External extern Global Program execution 0 or NULL

Static Variables Example:

#include <stdio.h> void counter() { static int count = 0; // Static variable count++; printf("Function called %d times\n", count); } int main() { for(int i = 0; i < 5; i++) { counter(); } return 0; }
Output:
Function called 1 times
Function called 2 times
Function called 3 times
Function called 4 times
Function called 5 times

7. Advanced Function Concepts

Function Pointers

Function pointers are variables that store the address of functions. They enable dynamic function calls and callback mechanisms.

Function Pointer Example:

#include <stdio.h> int add(int a, int b) { return a + b; } int multiply(int a, int b) { return a * b; } int main() { // Declare function pointer int (*operation)(int, int); // Point to add function operation = add; printf("Addition: %d\n", operation(5, 3)); // Point to multiply function operation = multiply; printf("Multiplication: %d\n", operation(5, 3)); return 0; }
Output:
Addition: 8
Multiplication: 15

Math Library Functions

C provides various mathematical functions in the math.h library:

Using Math Library Functions:

#include <stdio.h> #include <math.h> int main() { double x = 16.0, y = 2.5; printf("sqrt(%.1f) = %.2f\n", x, sqrt(x)); printf("pow(%.1f, %.1f) = %.2f\n", x, y, pow(x, y)); printf("sin(90 degrees) = %.2f\n", sin(90 * 3.14159/180)); printf("log(%.1f) = %.2f\n", x, log(x)); printf("ceil(%.1f) = %.0f\n", y, ceil(y)); printf("floor(%.1f) = %.0f\n", y, floor(y)); return 0; }
Output:
sqrt(16.0) = 4.00
pow(16.0, 2.5) = 128.00
sin(90 degrees) = 1.00
log(16.0) = 2.77
ceil(2.5) = 3
floor(2.5) = 2

8. Practical Programming Problems

Problem 1: Calculator using Functions

#include <stdio.h> float add(float a, float b) { return a + b; } float subtract(float a, float b) { return a - b; } float multiply(float a, float b) { return a * b; } float divide(float a, float b) { if(b != 0) return a / b; else { printf("Error: Division by zero!\n"); return 0; } } void displayMenu() { printf("\n=== Calculator ===\n"); printf("1. Addition\n"); printf("2. Subtraction\n"); printf("3. Multiplication\n"); printf("4. Division\n"); printf("5. Exit\n"); printf("Enter your choice: "); } int main() { int choice; float num1, num2, result; do { displayMenu(); scanf("%d", &choice); if(choice >= 1 && choice <= 4) { printf("Enter two numbers: "); scanf("%f %f", &num1, &num2); switch(choice) { case 1: result = add(num1, num2); break; case 2: result = subtract(num1, num2); break; case 3: result = multiply(num1, num2); break; case 4: result = divide(num1, num2); break; } if(choice != 4 || num2 != 0) { printf("Result: %.2f\n", result); } } } while(choice != 5); printf("Thank you for using the calculator!\n"); return 0; }

Problem 2: Prime Number Functions

#include <stdio.h> #include <stdbool.h> bool isPrime(int n) { if(n <= 1) return false; if(n <= 3) return true; if(n % 2 == 0 || n % 3 == 0) return false; for(int i = 5; i * i <= n; i += 6) { if(n % i == 0 || n % (i + 2) == 0) { return false; } } return true; } void printPrimesInRange(int start, int end) { printf("Prime numbers between %d and %d:\n", start, end); for(int i = start; i <= end; i++) { if(isPrime(i)) { printf("%d ", i); } } printf("\n"); } int countPrimesInRange(int start, int end) { int count = 0; for(int i = start; i <= end; i++) { if(isPrime(i)) count++; } return count; } int main() { int start = 10, end = 50; printPrimesInRange(start, end); printf("Total prime numbers: %d\n", countPrimesInRange(start, end)); // Test individual numbers int testNumbers[] = {17, 25, 29, 35, 41}; int size = sizeof(testNumbers) / sizeof(testNumbers[0]); printf("\nTesting individual numbers:\n"); for(int i = 0; i < size; i++) { printf("%d is %s\n", testNumbers[i], isPrime(testNumbers[i]) ? "prime" : "not prime"); } return 0; }
Output:
Prime numbers between 10 and 50:
11 13 17 19 23 29 31 37 41 43 47
Total prime numbers: 11

Testing individual numbers:
17 is prime
25 is not prime
29 is prime
35 is not prime
41 is prime

9. Best Practices and Common Mistakes

Best Practices

  • Use meaningful function names that describe what the function does
  • Keep functions small and focused - one function, one responsibility
  • Always declare function prototypes before using them
  • Use const for parameters that shouldn't be modified
  • Add comments to explain complex logic
  • Handle edge cases and error conditions
  • Use appropriate return types and parameter types

Common Mistakes to Avoid

  • Missing return statements in non-void functions
  • Forgetting to declare function prototypes
  • Infinite recursion due to missing or incorrect base cases
  • Not handling division by zero in mathematical functions
  • Modifying array parameters unintentionally
  • Incorrect parameter passing (value vs reference)
  • Not initializing local variables

Good vs Bad Function Examples:

// BAD: Poor function design int func(int x, int y) { int result; // Not initialized // Missing logic for some cases if(x > 0) { result = x + y; } return result; // May return garbage value } // GOOD: Well-designed function int calculateSum(int num1, int num2) { int sum = 0; // Properly initialized // Handle all cases sum = num1 + num2; return sum; // Always returns a valid value }

10. Summary and Key Takeaways

What We Learned Today

  • Functions are reusable blocks of code that perform specific tasks
  • Functions can have parameters and return values
  • Parameter passing can be by value or by reference
  • Arrays and strings are passed to functions as pointers
  • Recursion allows functions to call themselves
  • Variable scope determines where variables can be accessed
  • Static variables retain their values between function calls
  • Function pointers enable dynamic function selection

Practice Problems for Students

  1. Write a function to find the GCD of two numbers using recursion
  2. Create a function to sort an array using bubble sort
  3. Implement a function to check if a string is a palindrome
  4. Write a function to convert decimal to binary
  5. Create a function to find the second largest element in an array
  6. Implement a function to count the frequency of each character in a string
  7. Write a recursive function to solve Tower of Hanoi problem
  8. Create a function to perform matrix multiplication

Next Lecture Preview

In our next lecture, we'll explore Pointers in C, which will help you understand:

  • Memory addresses and pointer variables
  • Pointer arithmetic and operations
  • Dynamic memory allocation
  • Pointers with arrays and functions
  • Double pointers and pointer to pointers