UES103_L3
UES103_L3
• Write your own functions to avoid writing the same code segments multiple
times
• If you check several integers for primality in various places of your code,
just write a single primality-testing function, and call it on all occasions
• Use existing functions as building blocks for new programs
• Use functions without rewriting them yourself every time it is needed
• These functions may be written by you or by others (like sqrt(), printf())
int main()
int prime (int x)
{
{
int numb, flag, j=3;
int i, test;
scanf(“%d”,&numb);
i=2, test =0;
while (j <= numb) {
while ((i <= sqrt(x)) && (test ==0))
flag = prime(j);
{
if (flag == 0)
if (x%i==0) test = 1;
printf( “%d is prime\n”, j );
i++;
j++;
}
}
return test;
return 0;
}
}
Tracking the flow of control
Nested Functions
• A function cannot be defined within another function. It can be called
within another function.
• All function definitions must be disjoint.
• Call by reference
• Passes the address of the original argument to a called function.
• Execution of the function may affect the original argument in the calling function.
• Not directly supported in C, but supported in some other languages like C++.
• In C, you can pass copies of addresses to get the desired effect.
Parameter passing and return: 1
Recursion
Memory Layout
• We are going to dive deeper into different areas of
memory used by our programs.
• The stack is the place where all local variables and
parameters live for each function. A function’s stack
“frame” goes away when the function returns.
• The stack grows downwards when a new function is
called and shrinks upwards when the function is
finished.
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
} main
0x0
The Stack Memory
void func2() {
int d = 0;
}
void func1() {
int c = 99;
func2();
}
0x0
Recursion
4
Basis and Recursion
fact(n) = 1, if n = 0 /* Stopping
criteria */
= n * fact(n − if n > 0 /* Recursive form
1), */
5
Examples:
• Factorial:
fact(0) = 1
fact(n) = n * fact(n − 1), if n > 0
• Fibonacci sequence
(0,1,1,2,3,5,8,13,21,…) fib (0) = 0
fib (1) = 1
fib (n) = fib (n − 1) + fib (n − 2), if n > 1
5
Example 1 :: Factorial
5
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else {
return n * factorial(n – 1);
}
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else {
return n * factorial(n – 1);
}
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
} factorial
n: 3
int main(int argc, char *argv[]) {
printf("%d", factorial(4));
return 0;
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
} factorial
n: 3
int main(int argc, char *argv[]) {
printf("%d", factorial(4));
return 0;
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
} factorial
n: 3
int main(int argc, char *argv[]) {
printf("%d", factorial(4)); factorial
return 0; n: 2
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
} factorial
n: 3
int main(int argc, char *argv[]) {
printf("%d", factorial(4)); factorial
return 0; n: 2
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
} factorial
n: 3
int main(int argc, char *argv[]) {
printf("%d", factorial(4)); factorial
return 0; n: 2
}
factorial
n: 1
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
} factorial
n: 3
int main(int argc, char *argv[]) {
printf("%d", factorial(4)); factorial
return 0; n: 2
}
Returns 1 factorial
n: 1
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
} factorial
n: 3
int main(int argc, char *argv[]) {
printf("%d", factorial(4)); Returns 2 factorial
return 0; n: 2
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); n: 4
}
} Returns 6 factorial
n: 3
int main(int argc, char *argv[]) {
printf("%d", factorial(4));
return 0;
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else { factorial
return n * factorial(n – 1); Returns 24
n: 4
}
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else {
return n * factorial(n – 1);
}
}
0x0
The Stack Memory
Each function call has its own stack frame for its own copy of
variables.
main
int factorial(int n) { argc: 1
if (n == 1) { Stack
return 1; argv: 0xfff0
} else {
return n * factorial(n – 1);
}
}
0x0
The Stack
• The stack behaves like a…well…stack! A new function call pushes on
a new frame. A completed function call pops off the most recent
frame.
• Interesting fact: C does not clear out memory when a function’s
frame is removed. Instead, it just marks that memory as usable for
the next function call. This is more efficient!
• A stack overflow is when you use up all stack memory. E.g. a
recursive call with too many function calls.
• What are the limitations of the stack?
Example 1 :: Factorial Execution
fact( 2
4)
if (4 = = 1) return 4
(1); 6
else return (4 *
fact(3)); if (3 = = 1) return
(1); else return 2
(3 * fact(2));
if (2 = = 1) return
(1); else return 1
int fact ( int n) (2 * fact(1));
{
if (1 = = 1) return
if (n = = 1) return (1);
(1); else return
else
(1 * fact(0));
return (n * fact(n − 1) );
}
Example 2 :: Fibonacci number
Fibonacci number f(n) can be defined as:
f(0) = 0
f(1) = 1
f(n) = f(n − 1) + f(n − 2), if n > 1
• The successive Fibonacci numbers are:
0, 1, 1, 2, 3, 5, 8, 13, 21, …..
int f (int n)
{
if (n < 2) return (n);
else return ( f(n − 1) + f(n − 2) );
}
Tracing Execution
int f (int n)
{
f(4)
if (n < 2) return (n);
else return ( f(n − 1) + f(n − f(3) f(2)
2) );
}
f(2) f(1) f(1) f(0)
How many times is the function called when
evaluating f(4) ?
f(1) f(0)
Inefficiency:
• Same thing is computed called 9
several times. times
How are recursive calls implemented?
Actual
Parameters
Local Variables
Return Value
Return Address
...
73
Example:: main( ) calls fact(3)
main()
{
int n; n = 3;
printf (“%d \n”, fact(n) );
} int fact (n) int n;
{
if (n = = 0)
return (1);
else
return (n * fact(n-1));
}
TRACE OF THE STACK DURING EXECUTION
n=0
1
RA .. fact
n=1 n=1 n=1
fact( )
main( ) - - 1*1 = 1
returns to
calls RA .. fact RA .. fact RA .. fact main( )
fact( ) n=2 n=2 n=2 n=2 n=2
- - - - 2*1 = 2
RA .. fact RA .. fact RA .. fact RA .. fact RA .. fact
n=3 n=3 n=3 n=3 n=3 n=3 n=3
- - - - - - 3*2 = 6
RA .. main RA .. main RA .. main RA .. main RA .. main RA .. main RA .. main
75
Do Yourself
Trace the activation records for the following version of
Fibonacci sequence.
#include <stdio.h>
int f (int n)
{ Actual Parameters
int a, b; (n)
if (n < 2) return (n);
else { Local Variables
X a = f(n-1); (a, b)
Y b = f(n-2);
Return Value
return (a+b); }
} Return Address
(either main or f)
main( ) {
printf(“Fib(4) is: %d \n”, f(4));
}
76
Some points to note
Every recursive program can also be written without recursion
• Tail Recursion: Last thing a recursive function does is making a single recursive call (of
itself) at the end.
• Easy to replace tail recursion by a loop.
• In general, removal of recursion may be a very difficult task (even if you have your own
recursion stack).
Recursion can be helpful in many situations
• Better readability
• Ease of programming
• Sometimes, recursion gives best-possible or best-known algorithms to solve problems
Recursion can also be a killer
• You solve the same subproblem multiple times (Example: Fibonacci numbers)
• Every recursive call incurs a (small) overhead
Use recursion with caution
Example of tail recursion Call from main() as:
scanf(“%d”, &N);
Not a tail recursion: s = sum2(N, 0);
int sum1 ( int n )
{ Equivalent iterative function:
if (n == 0) return 0;
return n + sum1(n–1); int sum3 ( int n )
{
} int partialsum= 0;
while (n > 0) {
Tail recursion: partialsum = n + partialsum;
n = n – 1;
int sum2 ( int n, int partialsum ) }
{ return partialsum;
}
if (n == 0) return partialsum;
return sum2(n – 1, n + partialsum);
}
Important things to remember
• Think how the current problem can be solved if you can solve exactly the same problem on
one or more smaller instance(s).
• Do NOT think how the problem will be solved on smaller instances, just call the
function recursively and assume that the recursive calls do their jobs correctly.
• Do NOT forget to include the base cases to solve the problem on smallest instances.
• This is basically mathematical induction applied to programming.
61 49 145 100
sumSquares(5,6) sumSquares(7,7) sumSquares(8,9) sumSquares(10,10)
25 36 64 81
sumSquares(5,5) sumSquares(6,6) sumSquares(8,8) sumSquares(9,9)
25 36 49 64 81 100
Example: Printing the digits of an integer in reverse
Print the last digit, then print the remaining number in reverse
• Ex: If integer is 743, then reversed is print 3 first, then print the reverse of 74
14
Example: Printing your name in reverse
#include <stdio.h>
void readandprint ()
{
char c;
scanf("%c", &c);
if (c == '\n') return;
readandprint(); Output
printf("%c", c);
Enter your name and hit return:
} Jane Doe eoD enaJ
int main ()
{
printf("Enter your name and hit return: ");
readandprint(); Exercise: Rewrite this code so that
printf("\n"); the output looks as follows:
}
Enter your name and hit return:
Jane Doe Your name in reverse:
eoD enaJ
15
Counting Zeros in a Positive Integer
16
Common Errors in Writing Recursive Functions
int badFactorial(int x) {
Non-terminating Recursive Function (Infinite recursion)
return x *
badFactorial(x-1);
• No base }
case int badSum2(int x)
{
• The base case is never if(x==1) return 1;
reached return(badSum2(
x--));
}
int
anotherBadFactorial(int
x) { if(x == 0)
return
1; else
return x*(x-1)*anotherBadFactorial(x-2);
// When x is odd, base case is never
reached!!
}
Common Errors in Writing Recursive Functions
int anotherBadFactorial
(int x) { int i, fact = 0;
if (x == 0) return 1;
else {
for (i=x; i>0; i=i-1) {
fact = fact + x*anotherBadFactorial(x-1);
}
return fact;
}
}
1
2
3
4
5
20
Phase-1: Move top n – 1 from LEFT to CENTER
1
2
3
3 2 1
3 1
2
3 1
2
1
2 3
1
2 3
1 2 3
1
2
3
int main( )
{ int n; /* Number of disks */
scanf (“%d”, &n);
transfer (n, ‘L’, ‘R’, ‘C’);
return 0;
}
With 4
discs
Recursion versus Iteration
Repetition
• Iteration: explicit loop
• Recursion: repeated nested function calls
Termination
• Iteration: loop condition fails
• Recursion: base case recognized Both
can have infinite loops
Balance
• Understand the benefits / penalties of recursion in terms of
• Ease of implementation
• Readability
• Performance degradation / performance enhancement
• Take an educated decision
More Examples
What do the following programs print?
32
Paying with fewest coins
int canchange( int k )
{
int a;
if (k==0) return 0;
if ( (k ==3) || (k == 5) || (k == 10) )
return 1; if (k < 3) return –1 ;
a = canchange( k – 10 ); if (a > 0)
return a+1 ; a = canchange( k – 5 );
if (a > 0) return a+1 ; a =
canchange( k – 3 ); if (a > 0) return
a+1 ; return –1;
}
Exercise: Rewrite this code if the denominations are 3, 8, and 10. Do you see a
33
problem? Repair it.
Practice Problems
1. Write a recursive function to search for an element in an array
2. Write a recursive function to count the digits of a positive integer
(do also for sum of digits)
3. Write a recursive function to reverse a null-terminated string
4. Write a recursive function to convert a decimal number to binary
5. Write a recursive function to check if a string is a palindrome or
not
6. Write a recursive function to copy one array to another
• Note:
• For each of the above, write the main functions to call the recursive function
also
• Practice problems are just for practicing recursion, recursion is not
necessarily the most efficient way of doing them