0% found this document useful (0 votes)
14 views102 pages

UES103_L3

The document provides an overview of functions in C programming, including their declaration, definition, calling, and return mechanisms. It emphasizes the importance of functions for modular programming, code readability, and reusability, while also discussing local and global variable scopes, parameter passing techniques, and recursion. Additionally, it covers memory layout related to function calls and the stack's role in managing local variables.

Uploaded by

redadam876
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)
14 views102 pages

UES103_L3

The document provides an overview of functions in C programming, including their declaration, definition, calling, and return mechanisms. It emphasizes the importance of functions for modular programming, code readability, and reusability, while also discussing local and global variable scopes, parameter passing techniques, and recursion. Additionally, it covers memory layout related to function calls and the stack's role in managing local variables.

Uploaded by

redadam876
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/ 102

UES103

Programming for Problem Solving


Saif Nalband, PhD
1A27-31/1A42-46
Lab Password: cslab768
Functions: Declaration, Definition, Call and
return, Scope of variables, Storage classes,
Recursive functions, Recursion vs Iteration.
Functions
• Function
• A program segment that carries out a specific, well-defined task.
• Examples
• A function to find the gcd of two numbers
• A function to find the largest of n numbers

• A function will carry out its intended task whenever it is called


• Functions may call other functions (or itself)
• A function may be called multiple times (with different arguments)

• Every C program consists of one or more functions.


• One of these functions must be called “main”.
• Execution of the program always begins by carrying out the instructions
in “main”.
Why Functions?
• Functions allow one to develop a program in a modular fashion.
• Codes become readable
• Codes become manageable to debug and maintain

• 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())

• Abstraction: Hide internal details (library functions)


Use of functions: Area of a circle
Use of functions: Area of a circle
Defining a Function
Calling a function #include <stdio.h>
• Called by specifying the function name and
parameters in an instruction in the calling
function. /* Function to compute the area of a
circle */
• When a function is called from some other float myfunc (float r)
function, the corresponding arguments in {
the function call are called actual arguments
or actual parameters. float a;
a = 3.14159 * r * r;
• The function call must include a matching return a;
actual parameter for each formal parameter. }
• Position of an actual parameters in the
parameter list in the call must match the main()
position of the corresponding formal {
parameter in the function definition. float radius, area;
• The formal and actual arguments would
match in their data types. Mismatches are scanf (“%f”, &radius);
auto-typecasted if possible. area = myfunc (radius);
• The actual parameters can be expressions printf (“\n Area is %f \n”, area);
possibly involving other function calls (like }
f(g(x)+y)).
Function Prototypes: declaring a function
• Usually, a function is defined before it is called.
• main() is usually the last function in the program written.
• Easy for the compiler to identify function definitions in a single scan through the file.
• Some prefer to write the functions after main(). There may be functions that call each other.
• Must be some way to tell the compiler what is a function when compilation reaches a
function call.
• Function prototypes are used for this purpose
• Only needed if function definition comes after a call to that function.
• Function prototypes are usually written at the beginning of a program, ahead of any functions
(including main()).
• Prototypes must specify the types. Parameter names are optional (ignored by the compiler).
• Examples:
• int gcd (int , int );
• void div7 (int number);
• Note the semicolon at the end of the line.
• The parameter name, if specified, can be anything; but it is a good practice to use the
same names as in the function definition.
Example:
Function prototype / declaration
#include <stdio.h>
int sum( int, int );
int main( ) This program needs a function prototype or
{ function declaration since the function call
int x, y; comes before the function definition.
scanf(“%d%d”, &x, &y);
printf(“Sum = %d\n”, sum(x, y));
}
Function call
int sum (int a, int b)
{
return a + b; Function definition
}
Return value
• A function can return a single • Sometimes a function is not meant for
returning anything
value Using return statement
• Such functions are of type void
• Like all values in C, a function return value has a
type Example: A function which prints if a number
is divisible by 7 or not.
• The return value can be assigned to a variable in the
calling function
void div7 (int n)
{
int main( )
if ((n % 7) == 0)
{ printf (“%d divisible by 7”, n);
int x, y, s; else
scanf(“%d%d”, &x, &y); printf (“%d not divisible by 7”, n);
return;
s = sum(x, y);
}
}
int sum (int a, int b)
• The return type is void
{ • The return statement for void functions is
return a + b; optional at the end
The return statement
In a value-returning function, return does two
void compute_and_print_itax ()
distinct things:
{
• Specify the value returned by the execution of float income;
the function. scanf (“%f”, &income);
• Terminate the execution of the called function if (income < 50000) {
and transfer control back to the caller function. printf (“Income tax = Nil\n”);
return; /* Terminates function execution */
A function can only return one value. }
if (income < 60000) {
• The value can be any expression matching the
printf (“Income tax = %f\n”, 0.1*(income-50000));
return type.
return; /* Terminates function execution */
• It might contain more than one return statement. }
if (income < 150000) {
In a void function:
printf (“Income tax = %f\n”,0.2*(income-60000)+1000);
• "return” is optional at the end of the function return ; /* Terminates function execution */
body. }
• "return” may also be used to terminate printf (“Income tax = %f\n”,0.3*(income-150000)+19000);
execution of the function explicitly before }
reaching the end.
• No return value should appear following “return”.
Another Example: What is happening here?

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.

• Nested function calls are allowed.


• A calls B, B calls C, C calls D, etc.
• The function called last will be the first to return.

• A function can also call itself, either directly or in a cycle.


• A calls B, B calls C, C calls back A.
• Called recursive call or recursion.
Example: main( ) calls ncr( ), ncr( ) calls fact( )
Local variables
• A function can define its own local variables.
• The local variables are known (can be accessed) only within the
function in which they are declared.
• Local variables cease to exist when the function returns.
• Each execution of the function uses a new set of
local variables. Parameters are also local.
Revisiting nCr
Scope of a variable
• Part of the program from which the value of the variable can be used (seen).
• Scope of a variable - Within the block in which the variable is defined.
• Block = group of statements enclosed within { }
• Local variable – scope is usually the function in which it is defined.
• So two local variables of two functions can have the same name, but
they are different variables
• Global variables – declared outside all functions (even main).
• Scope is entire program by default, but can be hidden in a block if local
variable of same name defined
• You are encouraged to avoid global variables.
What happens here?
What Happens?
Local Scope replaces Global Scope
Parameter Passing
• When the function is called, the value of the actual parameter is
copied to the formal parameter.
Parameter Passing by Value in C

• Used when invoking functions

• Call by value / parameter passing by value


• Called function gets a copy of the value of the actual argument passed to the function.
• Execution of the function does not change the actual arguments.
• All changes to a parameter done inside the function are done on the copy.
• The copy is removed when the control returns to the caller function.
• The value of the actual parameter in the caller function is not affected.
• The arguments passed may very well be expressions (example: fact(n-r)).

• 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

void func1() { Stack argc: 1


int c = 99;
func2(); argv: 0xfff0
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); argv: 0xfff0
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
} func1

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
} func1

int main(int argc, char *argv[]) { c: 99


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
} func1

int main(int argc, char *argv[]) { c: 99


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
} func1

int main(int argc, char *argv[]) { c: 99


int a = 42;
int b = 17;
func1(); func2
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
} func1

int main(int argc, char *argv[]) { c: 99


int a = 42;
int b = 17;
func1(); func2
printf("Done.");
return 0; d: 0
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
} func1

int main(int argc, char *argv[]) { c: 99


int a = 42;
int b = 17;
func1(); func2
printf("Done.");
return 0; d: 0
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
} func1

int main(int argc, char *argv[]) { c: 99


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
} func1

int main(int argc, char *argv[]) { c: 99


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
} main

void func1() { Stack a: 42 argc: 1


int c = 99;
func2(); b: 17 argv: 0xfff0
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
The Stack Memory
void func2() {
int d = 0;
}

void func1() {
int c = 99;
func2();
}

int main(int argc, char *argv[]) {


int a = 42;
int b = 17;
func1();
printf("Done.");
return 0;
}

0x0
Recursion

A process by which a function calls itself repeatedly.


• Either directly.
• F calls F.
• Or cyclically in a chain.
• F calls G, G calls H, and H calls F.

Used for repetitive computations in which each action is stated in terms of


a previous result. fact(n) = n * fact (n-1)

4
Basis and Recursion

For a problem to be written in recursive form, two conditions are


to be satisfied:

• It should be possible to express the problem in recursive


form.

• The problem statement must include a stopping condition

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

• GCD (assume that m and n are non-negative


and m ≥ n): gcd (m, 0) = m
gcd (m, n) = gcd (n, m%n) , 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

int fact ( int n)


{
if (n = = 1)
return();
else
return(n * fact(n − 1));
}

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);
}
}

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 {
return n * factorial(n – 1);
}
}

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
}
}

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
}
}

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));
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
}
}

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 {
return n * factorial(n – 1);
}
}

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 {
return n * factorial(n – 1);
}
}

int main(int argc, char *argv[]) {


printf("%d", factorial(4));
return 0;
}

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?

What we have seen ….


• Activation record gets pushed into the stack when a function
call is made.
• Activation record is popped off the stack when the function
returns.

In recursion, a function calls itself.


• Several function calls going on, with none of the function calls
returning back.
• Activation records are pushed onto the stack
continuously.
• Large stack space required.
• Activation records keep popping off, when the termination condition of
recursion is reached.

We shall illustrate the process by an example of computing factorial.


• Activation record looks like:

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.

• When you write a recursive function


• First, write the terminating/base condition
• Then, write the rest of the function
• Always double-check that you have both
Example: Sum of Squares
Write a function that takes two integers m and n as arguments, and computes and
returns the sum of squares of every integer in the range [m:n], both inclusive.

int sumSquares (int m, int n)


{
int middle ;
if (m == n)
return(m*m);
else
{
middle = (m+n)/2;
return (sumSquares(m,middle) + sumSquares(middle+1,n));
}
}
int sumSquares (int m, int n)
Annotated Call Tree {
int middle ;
if (m == n) return(m*m);
else {
middle = (m+n)/2;
355 return (sumSquares(m,middle)
sumSquares(5,10) +
sumSquares(middle+1,n));
}
110 245
}
sumSquares(5,7) sumSquares(8,10)

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

void printReversed ( int i )


{
if (i < 10) {
printf(“%d\n”, i); return;
}
else {
printf(“%d”, i%10);
printReversed(i/10);
}
}

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

Check last digit from right


• If it is 0, number of zeros = 1 + number of zeroes in remaining part
of the number
• If it is non-0, number of zeros = number of zeroes in remaining part
of the number

int zeros (int number)


{
if(number < 10) return 0;
if (number % 10 == 0)
return( 1 + zeros(number/10) );
else
return( zeros(number/10) );
}

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

Mixing up loops and recursion

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;
}
}

In general, if you have recursive function calls within a loop, think


carefully if you need it. Most recursive functions you will see in this
course will not need this
Example :: Towers of Hanoi Problem
The problem statement:
• Initially all the disks are stacked on the LEFT pole.
• Required to transfer all the disks to the RIGHT pole.
• Only one disk on the top can be moved at a time.
• A larger disk cannot be placed on a smaller disk.
• CENTER pole is used for temporary storage of disks.

1
2
3
4
5

LEFT CENTER RIGHT


Recursive Formulation

Recursive statement of the general problem of n disks.


• Step 1:
• Move the top (n-1) disks from LEFT to CENTER.
• Step 2:
• Move the largest disk from LEFT to RIGHT.
• Step 3:
• Move the (n-1) disks from CENTER to RIGHT.

20
Phase-1: Move top n – 1 from LEFT to CENTER

1
2
3

LEFT CENTER RIGHT

3 2 1

LEFT CENTER RIGHT

3 1
2

LEFT CENTER RIGHT


Phase-2: Move the nth disk from LEFT to RIGHT

3 1
2

LEFT CENTER RIGHT

1
2 3

LEFT CENTER RIGHT


Phase-3: Move top n – 1 from CENTER to
RIGHT

1
2 3

LEFT CENTER RIGHT

1 2 3

LEFT CENTER RIGHT

1
2
3

LEFT CENTER RIGHT


#include <stdio.h>
void transfer (int n, char from, char to, char temp);

int main( )
{ int n; /* Number of disks */
scanf (“%d”, &n);
transfer (n, ‘L’, ‘R’, ‘C’);
return 0;
}

void transfer (int n, char from, char to, char temp)


{
if (n > 0) {
transfer (n-1, from, temp, to);
printf (“Move disk %d from %c to %c \n”, n, from, to);
transfer (n-1, temp, to, from);
}
return;
}
With 3 discs

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?

void foo( int n ) void foo( int n ) void foo( int n )


{ { {
int data; int data; int data;
if ( n == 0 ) if ( n == 0 ) return; if ( n == 0 )
return; foo ( n – 1 ); return;
scanf(“%d”, scanf(“%d”, scanf(“%d”,
&data); &data); &data);
foo ( n – 1 ); printf(“%d\n”, printf(“%d\n”, data);
printf(“%d\n”, data); data); foo ( n – 1 );
} } }
main ( ) main ( ) main ( )
{ int k = 5; { int k = { int k =
foo ( k ); 5; foo 5; foo
} ( k ); ( k );
} }
Printing cumulative sum -- will this work?

• int foo( int n )


• {
• int data, sum ;
• if ( n == 0 ) return 0;
Input: 1 2 3 4 5
• scanf(“%d”, &data);
• sum = data + foo ( n – 1 ); Output: 5 9 12 14
printf(“%d\n”, sum); 15
How to rewrite this so that the output is: 1 3 6
• return sum;
10 15 ?
• }
• main ( ) {
• int k = 5; foo ( k
);
• }
Printing cumulative sum (two ways)
int foo( int n )
void foo( int n, int sum )
{
{
int data, sum ;
int data ;
if ( n == 0 ) return 0;
if ( n == 0 ) return 0;
sum = foo ( n – 1 );
Input: 1 2 3 4 5 scanf(“%d”, &data);
scanf(“%d”, &data);
sum = sum + data;
sum = sum + data;
Output:1 3 6 10 15 printf(“%d\n”, sum);
printf(“%d\n”, sum);
return sum; foo( k – 1, sum ) ;
}
}
main ( ) {
main ( ) {
int k = 5; int k = 5;
foo ( k ); foo ( k, 0 );
}
}
Paying with fewest coins

• A country has coins of denomination 3, 5 and 10, respectively.


• We are to write a function canchange( k ) that returns –1 if it is not possible to pay a
value of k using these coins.
• Otherwise it returns the minimum number of coins needed to make the payment.

• For example, canchange(7) will return –1.


• On the other hand, canchange(14) will return 4 because 14 can be paid as 3+3+3+5 and
there is no other way to pay with fewer coins.
• Finally, 15 can be changed as 3+3+3+3+3, 5+5+5, 5+10, so canchange(15) will return 2.
Paying with fewest coins

int canchange( int k )


{
int a;
if (k==0) return 0;
if ( ) return 1;
if (k < 3) ;

a = canchange( ); if (a > 0) return ;


a = canchange(k – 5); if (a > 0) return ;
a = canchange( ); if (a > 0) return
; return –1;
}

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

You might also like