C lecture 4.2
C lecture 4.2
Functions can be invoked in two ways: Call by Value or Call by Reference. These two ways are generally differentiated by the
type of values passed to them as parameters.
Call By Value: In this parameter passing method, values of actual parameters are copied to function’s formal parameters and
the two types of parameters are stored in different memory locations. So any changes made inside functions are not
reflected in actual parameters of the caller.
Call by Reference: Both the actual and formal parameters refer to the same locations, so any changes made inside the
function are actually reflected in actual parameters of the caller.
Difference between call by value and call by reference:
While calling a function, we pass values of While calling a function, instead of passing the
variables to it. Such functions are known as values of variables, we pass address of variables
“Call By Values”. to the function known as “Call By References.
In this method, the value of each variable in In this method, the address of actual variables in
calling function is copied into corresponding the calling function are copied into the dummy
dummy variables of the called function. variables of the called function.
With this method, the changes made to the With this method, using addresses we would
dummy variables in the called function have no have an access to the actual variables and hence
effect on the values of actual variables in the we would be able to manipulate them.
calling function.
Call by value:
#include<stdio.h>
void swapx(int x, int y);
int main()
{
int a = 10, b = 20;
swapx(a, b);
printf("a=%d b=%d\n", a, b);
return 0;
}
void swapx(int x, int y)
{
int t;
t = x;
x = y;
y = t;
printf("x=%d y=%d\n", x, y);
}
Call by reference:
#include<stdio.h>
void swapx(int*, int*);
int main()
{
int a = 10, b = 20;
swapx(&a, &b);
printf("a=%d b=%d\n", a, b);
return 0;
}
void swapx(int *x, int *y)
{
int t;
t = *x;
*x = *y;
*y = t;
int main()
{
int i = 12;
printf("Factorial of %d is %d\n", i, factorial(i));
return 0;
}
#include <stdio.h>
int fibonacci(int i)
{
if(i == 0) {
return 0;
}
if(i == 1) {
return 1;
}
return fibonacci(i-1) + fibonacci(i-2);
}
int main()
{
int i;
for (i = 0; i < 10; i++)
{
printf("%d\t\n", fibonacci(i));
}
return 0;
}
Use of memory stack:
C has three different pools of memory.
static: global variable storage, permanent for the entire run of the program.
stack: local variable storage (automatic, continuous memory).
heap: dynamic storage (large pool of memory, not allocated in contiguous order).
Static memory:
Static memory persists throughout the entire life of the program, and is usually used to store things like global variables, or
variables created with the static clause.
Stack memory
The stack is used to store variables used on the inside of a function (including the main() function). It’s a LIFO, “Last-In,-First-
Out”, structure. Every time a function declares a new variable it is “pushed” onto the stack. Then when a function finishes
running, all the variables associated with that function on the stack are deleted, and the memory they use is freed up. This
leads to the “local” scope of function variables. The stack is a special region of memory, and automatically managed by the
CPU. Stack memory is divided into successive frames where each time a function is called, it allocates itself a fresh stack
frame.
Heap memory:
The heap is the diametrical opposite of the stack. The heap is a large pool of memory that can be used dynamically – it is also
known as the “free store”. This is memory that is not automatically managed .
• the heap is managed by the programmer, the ability to modify it is somewhat boundless
• in C, variables are allocated and freed using functions like malloc() and free()
• the heap is large, and is usually limited by the physical memory available
• the heap requires pointers to access it
Types of Recursion:
Recursive functions can be classified on the basis of :
a.) If the functions call itself directly or indirectly. – Direct / Indirect
b.) If an operation is pending at each recursive call. – Tail Recursive/ Not
c.) based on the structure of the function calling pattern. – Linear / Tree
Direct Recursion:
If a function explicitly calls itself it is called directly recursive. When the method invokes itself it is direct.
int testfunc(int num)
{
if (num == 0)
return 0;
else
return (testfunc(num - 1));
}
Here, the function ‘testfunc’ calls itself for all positive values of num.
Indirect Recursion:
This occurs when the function invokes other method which again causes the original function to be called again.
If a method ‘X’ , calls method ‘Y’, which calls method ‘Z’ which again leads to ‘X’ being invoked is called indirect recursive
or mutually recursive as well.
int testfunc1(int num)
{
if (num == 0)
return 0;
else
return (testfunc2(num - 1));
}
int testfunc2(int num2)
{
return testfunc1(num2 - 1);
}
Tail / Bottom Recursion:
A function is said to be tail-recursive, if no operations are pending when the recursive function returns to its caller.
Such functions, immediately return the return value from the calling function. It is an efficient method as compared to others, as
the stack space required is less and even compute overhead will get reduced. Recollect the previously discussed example,
factorial of a number. We had written it in non tail recursive way, as after call operation is still pending.
int fact(int n)
{
if (n == 1)
return 1;
else
return (n * fact(n - 1));
}
In order to make it tail recursive, information about pending tasks has to be tracked.
int fact(int n)
{
return (n * fact2(n - 1));
}
int fact2(int n, int result)
{
if (n == 1)
return result;
return fact2(n - 1, n * result);
}
Linear and Tree Recursion:
Depending on the structure the recursive function calls take, or grows it can be either linear or non linear.
It is linearly recursive when, the pending operations do not involve another recursive call to the function. Our Factorial
recursive function is linearly recursive as it only involves multiplying the returned values and no further calls to function.
Tree recursion is when, pending operations involve another recursive call to function.
For example – Fibonacci series, the pending operations have recursive call to the fib() recursive function to compute the
results.