KEMBAR78
Material On Functions and Pointers | PDF | Pointer (Computer Programming) | Parameter (Computer Programming)
0% found this document useful (0 votes)
10 views16 pages

Material On Functions and Pointers

Uploaded by

kelvinkinnara
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views16 pages

Material On Functions and Pointers

Uploaded by

kelvinkinnara
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Definition of Function in C

A function is a self-contained block of code that performs a specific task, can be called from
other parts of the program, and may return a value.
General form:
return_type function_name(parameter_list) {
// body of the function
return value; // (if return_type is not void)
}

Advantages of Using Functions


1. Modularity – Breaks a large program into smaller, manageable parts.
2. Reusability – A function can be reused in multiple programs without rewriting the
code.
3. Code Readability & Maintainability – Programs are easier to read, debug, and
modify.
4. Reduced Redundancy – Avoids repetition of code by calling the function whenever
needed.
5. Abstraction – Hides the internal details, only the interface (name, parameters, return
type) is visible.
6. Collaboration – Different programmers can work on different functions of the same
project.
Usage of Functions
● Library Functions: Predefined in C standard library (printf(), scanf(), sqrt(), strlen(),
etc.).
● User-defined Functions: Written by the programmer for specific tasks.

Example: User-defined function for factorial
#include <stdio.h>
// Function declaration
int factorial(int n);
int main() {
int num = 5;
printf("Factorial of %d is %d\n", num, factorial(num)); // Function call
return 0;
}
// Function definition
int factorial(int n) {
if (n == 0 || n == 1)
return 1;
else
return n * factorial(n - 1);
}

1. Function Declaration (Prototype/declaration/signature)


It tells the compiler about the function’s name, return type, and parameters before it is used.
It does not allocate memory or contain body.
Syntax:
return_type function_name(parameter_list);

1
Example:
int add(int a, int b); // Function declaration (prototype)
// this is a functino with the name “add” which returns ‘int’ and accepts two integer
arguments

2. Function Calling
When a function is invoked in the program, control transfers to that function, executes its
body, and then returns back to the calling point.
Syntax:
function_name(arguments);
Example:
sum = add(10, 20); // Function call
//here add function takes 2 arguments and returns the integer value(res) into sum variable

3. Function Definition
This is the actual body of the function where the logic is written.
Complete Example (All Three Together)
#include <stdio.h>
// 1. Function Declaration
int add(int, int);
int main() {
int x = 10, y = 20, result;
// 2. Function Calling
result = add(x, y);
printf("Sum = %d\n", result);
return 0;
}
// 3. Function Definition
int add(int a, int b) {
return a + b;
}

output:

Flow of Execution
1. Declaration → Informs compiler (int add(int, int);)
2. Calling → Transfers control (add(x, y);)
3. Definition → Executes the function body and returns value (return a + b;)
Difference Between Loop and Recursion
Aspect Loop Recursion

2
Definition A control structure that repeats a block A function that calls itself until a
of code until a condition is false (for, base condition is met.
while, do-while).
Termination Controlled by a condition in the loop (i Controlled by a base case.
< n). Without it → infinite recursion.
Memory Uses a single memory block; variables Each recursive call consumes a
Usage are reused. new stack frame (function call
overhead).
Performance Usually faster and more memory- Slower due to overhead of
efficient. repeated function calls.
Readability Better for simple repetitive tasks. More elegant and readable for
problems with repetitive
subproblems.
Risk Risk of infinite loop if condition is Risk of stack overflow if base
wrong. case is missing or too deep
recursion.
Examples Printing numbers, summing arrays, Factorial, Fibonacci, Tower of
iterating over data. Hanoi, Tree/Graph traversals.
When to Use Loop
Use loops when:
● The number of iterations is known or easily determined.
● Task is iterative and does not naturally break into smaller subproblems.
● Memory efficiency and speed are important (e.g., reading array elements, summing
values, printing sequences).
Example: Printing first 100 numbers is best done with a for loop.
When to Use Recursion
Use recursion when:
● Problem is recursive by nature (solution depends on smaller instances of the same
problem).
● Problems are easier to express recursively (mathematical definitions, divide-and-
conquer strategies).
● Used in data structures and algorithms like:
o Factorial and Fibonacci
o Tower of Hanoi
o QuickSort, MergeSort
o Tree/Graph traversals
Example: Traversing a binary tree (each node leads to left and right subtrees).
Proper Justification
● Loops are more efficient in terms of memory and execution speed, so they are
preferred for simple repetitive tasks.
● Recursion provides a clear and elegant solution for problems that are recursive in
nature, even though it may use more memory and be slightly slower.
● In fact, many recursive problems can be rewritten using loops, but recursion often
makes the code simpler and closer to the mathematical definition of the problem.
What is Recursion?
Definition: Recursion is a programming technique where a function calls itself directly or
indirectly to solve a problem.

3

Every recursive function has:
1. Base Case → Condition where recursion stops.
2. Recursive Case → Function calls itself with a smaller subproblem.
Example: Factorial using Recursion
Formula for factorial:

C Program
#include <stdio.h>

// Function declaration
long factorial(int n);

int main() {
int num;
printf("Enter a number: ");
scanf("%d", &num); //5

if (num < 0) {
printf("Factorial is not defined for negative numbers.\n");
} else if(num==0)
{
printf(“Factorial for 0 is 1”);
}
else
{
printf("Factorial of %d = %ld\n", num, factorial(num)); //recursive function calling
}
return 0;
}

// Recursive function definition


long factorial(int n) {
if (n == 0 || n == 1) // Base case
return 1;
else
return n * factorial(n - 1); // Recursive case
}
Execution Example
Input: 5
factorial(5) → 5 * factorial(4)
factorial(4) → 4 * factorial(3)
factorial(3) → 3 * factorial(2)
factorial(2) → 2 * factorial(1)

4
factorial(1) → 1 (base case)
Result = 5 * 4 * 3 * 2 * 1 = 120
output:

Advantages of Recursion
1. Simplicity & Readability – Complex problems can be expressed in fewer lines (e.g.,
Fibonacci, factorial, tree traversal).
2. Closer to Mathematical Definition – Problems like factorial, Fibonacci, GCD, etc.
are naturally recursive.
3. Useful for Recursive Data Structures – Simplifies algorithms for trees, graphs,
linked lists, etc.
4. Reduces Code Size – Eliminates repetitive code compared to iterative methods.
Limitations of Recursion
1. Higher Memory Usage – Each recursive call needs a new function call stack frame.
2. Slower Execution – Due to repeated function calls and return management.
3. Risk of Stack Overflow – If recursion goes too deep (e.g., very large input without
base case).
4. Sometimes Less Efficient – Problems like Fibonacci recursion recompute the same
values multiple times.
Recursive Program for Fibonacci Series
Fibonacci Definition:

Program
#include <stdio.h>

// Function declaration
int fibonacci(int n);

int main() {
int n, i;
printf("Enter the number of terms: ");
scanf("%d", &n);

printf("Fibonacci Series: ");


for (i = 0; i < n; i++) {
printf("%d ", fibonacci(i)); // Function call
}

5
return 0;
}

// Recursive function definition


int fibonacci(int n) {
if (n == 0)
return 0; // Base case
else if (n == 1)
return 1; // Base case
else
return fibonacci(n - 1) + fibonacci(n - 2); // Recursive case
}
Example Output
Enter the number of terms: 6
Fibonacci Series: 0 1 1 2 3 5

Program:

#include <stdio.h>
#include <math.h>
// Function to read array elements
void readArray(int arr[], int n) {
printf("Enter %d elements:\n", n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
}
// Function to calculate sum
int calculateSum(int arr[], int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
}
return sum;
}
// Function to calculate mean
float calculateMean(int arr[], int n) {
int sum = calculateSum(arr, n);
return (float)sum / n;
}
// Function to calculate standard deviation
float calculateStdDev(int arr[], int n) {
float mean = calculateMean(arr, n);
float variance = 0.0;
for (int i = 0; i < n; i++) {
variance += pow(arr[i] - mean, 2);
}
variance = variance / n; // Population standard deviation
return sqrt(variance);

6
}
int main() {
int n,sum;
float mean,stdDev;
printf("Enter number of elements: ");
scanf("%d", &n);
int arr[n];

readArray(arr, n);
sum = calculateSum(arr, n);
mean = calculateMean(arr, n);
stdDev = calculateStdDev(arr, n);
printf("\nSum of elements = %d", sum);
printf("\nMean of elements = %.2f", mean);
printf("\nStandard Deviation of elements = %.2f\n", stdDev);
return 0;
}
Sample Output
Enter number of elements: 5
Enter 5 elements:
10 20 30 40 50
Sum of elements = 150
Mean of elements = 30.00
Standard Deviation of elements = 14.14

What is a Pointer in C?
A pointer is a variable that stores the memory address of another variable.
● If int x = 10;, then a pointer to x stores the address of x.
● Pointers are declared using the * symbol.
Example:
int x = 10;
int *p = &x; // p stores the address of x
Here:
● x → variable (value = 10).
● &x → address of x.
● p → pointer to x.
● *p → value stored at address in p (i.e., 10).
Reference Operator (&)
● Symbol: &
● Meaning: Address-of operator
● Purpose: Used to get the address of a variable.
Example:
int a = 5;
printf("Address of a = %p\n", (void*)&a);
Output (example):
Address of a = 0x7ffee3a3a8

7
Dereference Operator (*)
● Symbol: *
● Meaning: Indirection operator
● Purpose: Used to access the value stored at the address a pointer points to.
Example:
int a = 5;
int *p = &a;
printf("Value at address p = %d\n", *p);
Output:
Value at address p = 5

Difference Between & and *


Operator Symbol Meaning Usage Example Output
Reference & Gives address of &a if a=5 Address (e.g.,
variable 0x7ff...)
Dereference * Gives value at address If p=&a, then 5
*p
& can be used on both variables and pointers
but * can be used only on pointer variables.

Example Program
#include <stdio.h>
int main() {
int x = 10;
int *p = &x; // pointer stores address of x
printf("x = %d\n", x);
printf("Address of x (&x) = %p\n", (void*)&x);
printf("Pointer p = %p\n", (void*)p);
printf("Value at *p (dereference) = %d\n", *p);
return 0;
}
Example Output
x = 10
Address of x (&x) = 0x7ffeea02ac
Pointer p = 0x7ffeea02ac
Value at *p (dereference) = 10

Advantages of Pointers in C
1. Dynamic Memory Allocation: Enables use of malloc(), calloc(), realloc(), free().
2. Efficient Array and String Handling: Access and manipulate elements directly.
3. Function Arguments (Call by Reference): Allows modification of variables inside
functions.

8
4. Building Data Structures: Linked lists, trees, graphs rely on pointers.
5. Memory Efficiency: Avoids copying large structures, just pass pointer.
6. Flexibility: Enables dynamic resizing, efficient handling of large datasets.
7. Pointer Arithmetic: Enables traversing arrays quickly without indexing overhead.
Pointer Arithmetic in C
When you increment or decrement a pointer, it moves by the size of the data type it points
to, not just 1 byte.
Example:
int *p;
p++; // moves forward by sizeof(int) bytes
If int = 4 bytes, p increases by 4 in memory.
Rules of Pointer Arithmetic:
● p++ → Move to next element.
● p-- → Move to previous element.
● p + n → Move forward by n elements.
● p - n → Move backward by n elements.
● p2 - p1 → Gives number of elements between two pointers (of same array).

#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // pointer to first element
printf("Pointer Arithmetic Demo:\n");
printf("Value at p = %d\n", *p); // arr[0]
p++; // move to next element
printf("After p++ : %d\n", *p); // arr[1]
p = p + 2; // move 2 steps forward
printf("After p + 2 : %d\n", *p); // arr[3]
p--; // move one step back
printf("After p-- : %d\n", *p); // arr[2]
return 0;
}
Sample Output:
Pointer Arithmetic Demo:
Value at p = 10
After p++ : 20
After p + 2 : 40
After p-- : 30

9
Double Pointer
Double pointers are those pointers which stores the address of another pointer. The
first pointer is used to store the address of the variable, and the second pointer is used to store
the address of the first pointer. That is why they are also known as a pointer to pointer.

Feature Single Pointer (*p) Double Pointer (**pp)


Stores Address of a variable Address of a pointer
Indirection One level (*p) → value Two levels (**pp) → value of variable via pointer
level of variable
Usage Accessing variables Dynamic memory allocation (e.g., malloc),
pointer to pointer, arrays of pointers

Example program:

num
42
x1

p
x1
x2

pp
x2
x3

10
#include <stdio.h>
int main() {
int num = 42;
// Single pointer
int *p = &num;
// Double pointer
int **pp = &p;
printf("Value of num : %d\n", num);
printf("Address of num (&num) : %p\n", (void*)&num);
printf("\nUsing single pointer (p):\n");
printf("Value stored in p (address of num) : %p\n", (void*)p);
printf("Value at *p (value of num) : %d\n", *p);
printf("\nUsing double pointer (pp):\n");
printf("Value stored in pp (address of p) : %p\n", (void*)pp);
printf("Value at *pp (value stored in p) : %p\n", (void*)*pp);
printf("Value at **pp (value of num) : %d\n", **pp);
return 0;
}

Output:

11
DMA-largest value by using pointer array (array of pointers)

#include <stdio.h>
#include <stdlib.h>
int main() {
int n, i;
int **ptrArray; // array of pointers
printf("Enter number of elements: ");
scanf("%d", &n);
// Allocate memory for array of pointers
ptrArray = (int **)malloc(n * sizeof(int *));
if (ptrArray == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Allocate memory for each element and read values
for (i = 0; i < n; i++) {
ptrArray[i] = (int *)malloc(sizeof(int));
if (ptrArray[i] == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
printf("Enter element %d: ", i + 1);
scanf("%d", ptrArray[i]);
}
// Find the largest element
int *largest = ptrArray[0];
for (i = 1; i < n; i++) {
if (*ptrArray[i] > *largest) {
largest = ptrArray[i];
}
}
printf("\nThe largest element is: %d\n", *largest);
// Free allocated memory
for (i = 0; i < n; i++) {
free(ptrArray[i]);
}
free(ptrArray); //deallocation of allotted memory
return 0;

12
}
Sample output:
Enter number of elements: 5
Enter element 1: 12
Enter element 2: 7
Enter element 3: 45
Enter element 4: 23
Enter element 5: 18
The largest element is: 45

Dynamic Memory Allocation in C


In C, dynamic memory allocation allows us to allocate memory at runtime (instead of
compile-time). This is useful when the size of data structures (like arrays, linked lists, etc.) is
not known in advance.
Do We Need Dynamic Memory Allocation in C?
In static memory allocation (like arrays declared normally), memory is allocated at
compile-time, and its size is fixed.
But in many real-world cases, we don’t know the size of data structures in advance. That’s
where Dynamic Memory Allocation (DMA) comes in.
1. Unknown Size at Compile-Time
Example:
● If you’re writing a program to store student marks, you may not know how many
students will register.
● With static arrays:
● int marks[100]; // fixed size, waste if only 10 students
● With dynamic allocation:
● int *marks;
● marks = (int*) malloc(n * sizeof(int)); // size decided at runtime
This saves memory and makes the program flexible.
2. Efficient Memory Usage (Avoid Wastage)
● Static arrays reserve memory whether used or not.
● DMA allocates exact amount of memory needed at runtime, and you can also free it
when no longer needed.
3. Resizing Data Structures
● With realloc(), you can expand or shrink memory when requirements change.
● Example: Dynamic arrays that grow as users keep entering data (like vector in C++).
4. Building Complex Data Structures
DMA is essential for creating:
● Linked Lists
● Trees
● Graphs
● Hash Tables
These structures require nodes/blocks to be created dynamically as data comes in.
5. Lifetime Control
● Memory allocated statically exists throughout program execution.

13
● With DMA, you control the lifetime of memory using malloc() and free().
Real-Life Example Analogy
Imagine booking seats in a cinema hall:
● Static allocation = The manager reserves 100 seats for you in advance, whether
people come or not. Wasteful if only 20 come.
● Dynamic allocation = You book exactly as many seats as people arrive, and you can
even add more later if more people show up.
Dynamic memory allocation is needed for flexibility, efficiency, resizing, and
implementing advanced data structures.
Dynamic memory functions are defined in <stdlib.h>.
1. malloc()
Syntax:
ptr = (castType*) malloc(size_in_bytes);
● Allocates a block of memory of given size (in bytes).
● Returns a pointer to the first byte of the allocated block.
● Contents are not initialized (contain garbage values).
● Returns NULL if allocation fails.
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, *arr;
printf("Enter number of elements: ");
scanf("%d", &n);
arr = (int*) malloc(n * sizeof(int)); // Allocate memory for n integers
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
printf("Enter %d integers:\n", n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
printf("You entered: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
free(arr); // Free allocated memory
return 0;
}

2. calloc()
Syntax:
ptr = (castType*) calloc(num_elements, size_of_each);
● Allocates memory for an array of elements.
● Initializes all bytes to zero.

14
● Returns NULL if allocation fails.
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, *arr;
printf("Enter number of elements: ");
scanf("%d", &n);
arr = (int*) calloc(n, sizeof(int)); // Allocate and initialize with 0
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
printf("Default values (after calloc): ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]); // All will be 0 initially
}
free(arr);
return 0;
}

3. realloc()
Syntax:
ptr = (castType*) realloc(old_ptr, new_size_in_bytes);
● Changes the size of previously allocated memory (malloc/calloc).
● If new_size > old size, new memory is allocated (old contents preserved, extra
memory uninitialized).
● If new_size < old size, memory is truncated.
● If allocation fails, returns NULL.
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr, n;
printf("Enter initial size: ");
scanf("%d", &n);
arr = (int*) malloc(n * sizeof(int));
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
printf("Enter %d integers:\n", n);
for (int i = 0; i < n; i++) scanf("%d", &arr[i]);
printf("Enter new size: ");
scanf("%d", &n);
arr = (int*) realloc(arr, n * sizeof(int)); // Resize memory
if (arr == NULL) {
printf("Reallocation failed!\n");

15
return 1;
}
printf("Enter %d integers:\n", n);
for (int i = 0; i < n; i++) scanf("%d", &arr[i]);
printf("Final array: ");
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
free(arr);
return 0;
}

4. free()
Syntax:
free(ptr);
● Deallocates the memory allocated by malloc(), calloc(), or realloc().
● Prevents memory leaks.
● After freeing, the pointer becomes a dangling pointer → good practice is to set it to
NULL.
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int*) malloc(5 * sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
for (int i = 0; i < 5; i++) ptr[i] = i + 1;
printf("Allocated values: ");
for (int i = 0; i < 5; i++) printf("%d ", ptr[i]);
free(ptr); // Free allocated memory
ptr = NULL; // Avoid dangling pointer
return 0;
}

Summary Table
Function Syntax Initialization Use Case
malloc (type*) malloc(size) Garbage values Single block allocation
calloc (type*) calloc(n, size) All zeros Allocate array & init to 0
realloc (type*) realloc(ptr, new_size) Preserves old data Resize memory
free free(ptr) Not applicable Deallocate memory

16

You might also like