Functions with input arguments

Modular Programming
Advantages of using functions (to make a modular
program) are:
•Changing the program into separate pieces
•Code reusing
•Easier modification and maintenance of the
program
•More understandable program
•Functions can be called from anywhere in the
main program with different input data
•Functions enable the programmers to generate
their own libraries of the functions.
How is the information exchanging between the
main function and function subprograms?
Functions with input arguments
•The arguments of the functions are used to carry
information between the function subprogram and
the main function
•Input arguments carry information into the
function subprogram
•Output arguments are the return results of the
functions
Examples:
c = factorial(n) / (factorial(r)*factorial(n-r));
print_rboxed(135.68);
actual parameters
• Calling print_rboxed(135.68)
/*
* Displays a real number in a box.
*/
void
print_rboxed(double rnum)
{
printf("***********\n");
printf("*
*\n");
printf("* %7.2f *\n", rnum);
printf("*
*\n");
printf("***********\n");
}
***********
*
*
* 135.68 *
Formal Parameter
*
*
***********
/*
* Computes n! for n greater than or equal to zero
*/
int
indicates function result is an integer number
factorial(int n)
{
int i,
/* local variables */
product = 1;
/* Computes the product n x (n-1) x (n-2) x ... x 2 x 1 */
for (i = n; i > 1; --i) {
product *= i;
}
/* Returns function result */
return (product);
}
Functions with input arguments
• The local and formal parameters of a
function cannot be referenced by name in
other functions
• Functions can not reference the variables
defined in the main by the name
• Functions can use the constant names
(#define ..) if they are in the same file. Also
functions should have been defined after
the constant names definition
Syntax of function definition
function interface comment
return-type function-name(formal parameter declaration list)
{
local variable declarations
executable statements
}
Example:
/* Finds the larger of two numbers */
double
bigger(double n1, double n2)
{
double larger;
if (n1 > n2) larger=n1;
else larger=n2;
return(larger);
}
•A function can be called with different actual parameters.
for example for finding the number of combinations of selecting 2
items from 6 items without regards to order main program calls
factorial 3 times:
c = factorial(6) / (factorial (2) * factorial(6-2);
• Calling factorial with n=6 in the main program
int
factorial(int n) value of 6 copies into n
{
int i,
/* local variables */
product = 1;
/* Computes the product n x (n-1) x (n-2) x ... x 2 x 1 */
for (i = n; i > 1; --i) {
product *= i;
}
/* Returns function result */
return (product); Returns the result value of 720
}
Logical Functions
The type int result by a function can be interpret as a logical
value:
Function That Checks Whether a Value Is Even
/*
* Indicates whether or not num is even (divisible by 2):
* returns 1 if it is, 0 if not
*/
int
even(int num)
{
int ans;
ans = ((num % 2) == 0);
return (ans);
}
Function with Multiple Arguments
/*
* Multiplies its first argument by the power of 10 specified
* by its second argument.
*/
double
scale(double x, int n)
{
double scale_factor; /* local variable */
scale_factor = pow(10, n);
return (x * scale_factor);
}
• A function can be tested by a program (driver program)
included main function and a call to that function
Testing Function scale
/*
* Tests function scale.
*/
#include <math.h>
/* Function prototype */
double scale(double x, int n);
int
main(void)
{
double num 1;
int num 2;
/* Get values for num 1 and num 2 */
printf("Enter a real number> ");
scanf("%lf", &num 1);
printf("Enter an integer> ");
scanf("%d", &num 2);
/* Call scale and display result. */
printf("Result of call to function scale is %.3f\n",
scale(nu_m 1, num 2));
return (0);
double
scale(double x, int n)
{
double scale_factor; /* local variable - 10 to power n */
scale_factor = pow(10, n);
return (x * scale_factor);
}
---------------Output:
Enter a real number> 2.5
Enter an integer> -2
Result of call to function scale is 0.025
------------------- After calling function scale in the main
Function main data area Function scale data area
num_1 = 2.5
x=2.5
num_2= -2
n=-2
scale_factor=?
Argument List Correspondence
• The number of actual arguments in a call to a
function must be the same as the number of
formal arguments listed in the function definition
• The order of actual argument must be the same
as the order of formal arguments
• Each actual argument must be of a data type
that can be assigned to the corresponding
formal parameter with no unexpected loss of
information
Case Study with Top-Down Design
• Problem: Write a program that finds the
smallest divisor of a number or determines
that the number is a prime number.
• Analysis
Constant: NMAX 1000
Input: int n
Output: int min_div
•
Design Algorithm:
1. Get the number to check whether it is prime
1.1 Get a value for n
1.2 if n < 2
Display an error message
else if n <= NMAX
Do Step 2 and 3
else
Display an error message
2. Find smallest divisor other than 1, or determine number is
prime
3. Display smallest divisor or a message number is prime
3.1 if the smallest divisor is n
Display a message that n is prime
else
Display the smallest divisor of n
#include <stdio.h>
#define NMAX 1000
Int main(void)
{
int n, /* ckeck for prime*/ min_div; /* minimum divisor */
/* Gets a number to test.
*/
printf("Enter a number that you think is a prime number> ");
scanf("%d", &n);
/* Checks that the number is in the range 2...NMAX
if (n < 2) {
printf("Error: number too small. The smallest prime is 2.\n");
} else if (n <= NMAX) {
/* Finds the smallest divisor (> 1) of n */
min_div = find_div(n);
/* Displays the smallest divisor or a message that n is prime. */
if (min_div == n)
printf("%d is a prime number.\n", n);
else
printf("%d is the smallest divisor of %d.\n", min_div, n);
} else {
printf("Error: largest number accepted is %d.\n", NMAX);
}
return (0);
}
*/
Step 2 as a sub-problem: Function find_div
• Analysis
 Input (formal parameter)
int n
 Output (result to return)
int divisor
 Local variable
int trial
• Design
1. if n is even, set divisor to 2, otherwise, set
divisor to 0 and trial to 3
2. As long as divisor is 0, keep trying odd integers
(trial). If a divisor is found, store it in divisor. If trial
exceeds , store n in divisor.
3. Return divisor
#include <math.h>
/* Finds the smallest divisor of n between 2 and n (n is greater than 1) */
Int find_div(int n)
{
int trial, /* current candidate for smallest divisor of n
*/
divisor; /* smallest divisor of n; zero means divisor not yet found */
/* Chooses initialization of divisor and trial depends on n being even or odd. */
if (even(n)) {
divisor = 2;
} else {
divisor = 0;
trial = 3;
}
/* Tests each odd integer as a divisor of n until a divisor is found */
while (divisor == 0) {
if (trial > sqrt(n))
divisor = n;
else if ((n % trial) == 0)
divisor = trial;
else
trial += 2;
}
return (divisor); /* Returns problem output to calling module. */
}
Function Output Parameters with
Pointers
• So far the return part of a function ,at most could
only return one result value.
• By using pointers functions can return multiple
result values to the caller
• So far the input argument values could be
passed only by the values. It means the value of
actual parameters are copied into the value of
formal parameters and call to the functions can
not change value of arguments.
• By using pointers the address of the arguments
can be passed to the functions and the value of
these arguments can be changed by the
functions. For example the second argument of
the scanf or fscanf is passing by address when
calling scanf of fscanf
Pointers
• A pointer is a variable whose value is the address of a
memory cell that reserved for another variable
For example: int *nump means nump is a pointer to
another variable of type int:
n
nump
84
1024
• Each pointer has two parts:
1- Direct value of a pointer, which is an address of a
memory cell. For example nump is 1024
2- Indirect value of a pointer, which is the value of the
memory cell whose address is the pointer’s direct value.
For example *nump is 84
Meaning of * Symbol
“*” can be used for all of these purposes
1. Binary operator. For example: 2 * 3 means 2
times 3
2. For file pointer. For example FILE *inp; means
inp is a pointer to file
3. Definition of the pointers. For example:
char *singp; means signp is of type “pointer to
char”. Note that signp is address of a memory
cell.
4. Unary operator. for example “*nump” gets 84
which is the int value and can be used in the
expressions. Also “*singp” is a char value
such as ‘a’ or ‘b’
Example of a function that uses arguments of the type of pointers
/*Separates a number into three parts: a sign (+, -, or blank), a whole number
magnitude, and a fractional part.*/
void
separate(double num, /* input - value to be split
*/
char *signp, /* output - sign of num
*/
int *wholep, /* output - whole number magnitude of num*/
double *fracp) /* output - fractional part of num
*/
{
double magnitude; /* local variable - magnitude of num
*/
/* Determines sign of num */
if (num < 0)
*signp = '-';
else if (num == 0)
*signp = ' ';
else
*signp = '+';
/* Finds magnitude of num (its absolute value) and
separates it into whole and fractional parts
*/
magnitude = fabs(num);
*wholep = floor(magnitude);
*fracp = magnitude - *wholep;
}
Example of a program that calls a function with multiple output
arguments
/* Demonstrates the use of a function with input and output parameters.*/
#include <stdio.h>
#include <math.h>
void separate(double num, char *signp, int *wholep, double *fracp);
int
main(void)
{
prototype of separate
double value; /* input - number to analyze
*/
char sn; /* output - sign of value
*/
int whl; /* output - whole number magnitude of value
*/
double fr; /* output - fractional part of value
*/
/* Gets data */
printf("Enter a value to analyze> ");
scanf("%lf", &value);
Enter a value to analyze> 35.817
/* Separates data value into three parts */
Parts of 35.8170
separate(value, &sn, &whl, &fr);
sign: +
/* Prints results
*/
whole number magnitude: 35
printf("Parts of %.4f\n sign: %c\n", value, sn);
fractional part: 0.8170
printf(" whole number magnitude: %d\n", whl);
printf(" fractional part: %.4f\n", fr);
return (0);
} separate should be defined here….
Side effect of function call separate(value, &sn, &whl,&fr)
Function main
Data Area
Function separate
Data Area
value
value
35.817
35.817
sn
sign
7421
?
whl
wholep
?
7422
fr
fracp
?
7423
magnitude
?
Address
of sn =
&sn
More on Functions
• When a pointer is passed to a function it can be used as
“output” or “input/output” parameter.
• Scope of the name refers to the region of a program
where the name is visible
• For example scope of the formal and local
variables, is from their declaration to the closing brace of a
function in which they are declared
• The scope of predefine constants is the whole
program (they are global)
• If the same name is repeated to declare the variable one
of them may shadow another one. For example in the
following example fun-two can be called by one, main and
itself but function one can only ba called by main and
itself not fun-two
#define MAX 950
#define LIMIT 200
void one(int anarg, double second); /* prototype 1 */
int fun_two(int one, char anarg);
/* prototype 2 */
int
main(void)
{
int localvar;
both of two functions one and fun_two can be called here
...
} /* end main */
void
one(int anarg, double second)
/* header 1 */
{
int onelocal;
/* local 1 */ function fun-two can be called here
...
} /* end one */
int
fun_two(int one, char anarg)
/* header 2 */
{
int localvar;
/* local 2 */ function one can not be called here
...
} /* end fun_two */
Using formal parameters as actual parameters
void scan_fraction(int *nump, /* output - numerator */
int *denomp) /* output - denominator */
{
char slash; /* local - character between numerator & denominator
int status; /* status code returned by scanf indicating number
of valid values obtained
*/
int error; /* flag indicating whether or not an error has been
detected in current input
*/
char discard; /* unprocessed character from input line
do {
/* No errors detected yet */
error = 0;
/* Get a fraction from the user
*/
status = scanf("%d%c%d", nump, &slash,denomp );
……………
} while (error);
}
*/
*/
Recursive Functions
• A function that calls itself is said to be recursive
• Also if f1 calls f2 and f2 calls f1, f1 is considered
as a recursive function
• A recursive algorithm has the following form:
if this is a simple case
solve it
else
redefine the problem using recursion
• For the simple case of the problem a
straightforward, non-recursive solution is known
Recursive Functions
• For example to calculate the multiplication of 6
by 3 when only we allowed to use the addition
operation:
• The simple case is multiplication of 6 by 1
• And the recursive algorithm is:
1. Multiply 6 by 2.
1.1 Multiply 6 by 1
simple case
1.2 Add 6 to the result
2. Add 6 to the result of problem 1
Recursive Functions
/*
* Performs integer multiplication using + operator.
* Assumes n > 0
*/
int
multiply(int m, int n)
{
int ans;
if (n == 1)
ans = m; /* simple case */
else
ans = m + multiply(m, n - 1); /* recursive step */
return (ans);
}
18
multiply (6,3)
Tracing a recursive function
m is 6
n is 3
3 == 1 is false
ans is 6 + multiply(6, 2)
return ans
12
m is 6
n is 2
2 == 1 is false
ans is 6 + multiply(6, 1)
return ans
6
Each activation
frame
corresponding to
each function call
m is 6
n is 1
1 == 1 is true
ans is 6
return ans
1
2
3
4
5
6
m
n
ans
6
3
?
6
2
?
6
3
?
6
1
?
6
2
?
6
3
?
6
1
6
6
2
?
6
3
?
6
2
?
6
3
?
6
2
12
6
3
?
Local Variable and
Parameter Stack of
multiply(6,3)
7
m
n
ans
6
3
?
return from the third call
m
n
8
6
3
ans
18
return from the first call
return from the second call
Tracing a Recursive Function
int
multiply(int m, int n)
{
int ans;
printf("Entering multiply with m = %d, n = %d\n", m, n);
if (n == 1)
ans = m; /* simple case */
else
ans = m + multiply(m, n - 1); /* recursive step */
printf("multiply(%d, %d) returning %d\n", m, n, ans);
return (ans);
}
Entering multiply with m = 8, n = 3
Entering multiply with m = 8, n = 2
Entering multiply with m = 8, n = 1
multiply(8, 1) returning 8
multiply(8, 2) returning 16
multiply(8, 3) returning 24
printf is used
to make a selftrace recursive
function
Case Study: Bisection Method for
Finding Roots
• Problem: Develop a function bisect that
approximates a root of a function f on an
interval that contains an odd number of
roots
• Analysis
Input: double x_l, x_r; /* endpoints of interval */
double epsilon;
/* error tolerance */
Output: double root; /* approximate root of f */
Variable: double x_mid; /*interval midpoint */
Design: Algorithm
1. if this is a simple case, solve it
1.1 simple case 1 if interval shorter than epsilon,
return the midpoint
1.2 simple case 2 if function value at midpoint
is zero, return midpoint
else redefine the problem using recursion
1.3 bisect interval and execute a recursive call on
the half interval that contains the root
1.3.1 if f(x_l)*f(x_mid)<0
1.3.2 Find the root by bisecting[x_l,x_mid]
1.3.3 Find the root by bisecting[x_mid, x_r]
Implementation:
double
bisect(double x_l,
/* input - endpoints of interval in which
double x_r,
/*
to look for a root */
double epsilon)
/* input - error tolerance */
{
double root,
/* approximate root */
x_mid; /* interval midpoint */
/* Compute midpoint of interval
*/
x_mid = (x_l + x_r) / 2.0;
if (x_r - x_l < epsilon)
/* simple case 1 */
root = x_mid;
else if (f(x_mid) == 0.0)
/* simple case 2 */
root = x_mid;
else if (f(x_l) * f(x_mid) < 0.0) /* root in [x_l, x_mid] */
root = bisect(x_l, x_mid, epsilon);
else
/* root in [x_mid, x_r] */
root = bisect(x_mid, x_r, epsilon);
return (root);
}
*/
Case Study: Performing Arithmetic
Operations on Common Fractions
• Problem: Write a program that will allow
you to add, subtract, multiply, and divide
common fractions. The program will
prompt you for a fraction, an operator, and
another fraction and then display the
problem and the result. The process will
be repeated until you enter an n to answer
the question,
Continue? (y/n)
Analysis
Input: int n1, d1; /* numerator, denominator of
first fraction */
int n2, d2; /* second fraction */
char op; /* arithmetic operator + - * / */
char again; /* whether to continue */
Output: int n_ans; /* numerator of answer */
int d_ans; /* denominator of answer */
Design
1. Initialize again to y
2. As long as user wants to continue
3. Get a fraction problem
3.1 Get first fraction
3.2 Get operator
3.3 Get second fraction
4. Compute the result
4.1 Select a task based on operator
4.2 Put the result fraction in reduced form
5. Display problem and result
6. Check if user wants to continue
Implementation (Step 4 of the Algorithm)
/* Gets a fraction problem
*/
scan_fraction(&n1, &d1);
op = get_operator();
scan_fraction(&n2, &d2);
/* Computes the result
*/
switch (op) {
case '+':
add_fractions(n1, d1, n2, d2, &n_ans, &d_ans);
break;
case '-':
add_fractions(n1, d1, -n2, d2, &n_ans, &d_ans);
break;
case '*':
multiply_fractions(n1, d1, n2, d2, &n_ans, &d_ans);
break;
case '/':
multiply_fractions(n1, d1, d2, n2, &n_ans, &d_ans);
}
reduce_fraction(&n_ans, &d_ans);
Testing
• A stub is a skeleton function that consists of a
header, trace messages and assigned values to
the output parameters.
• Using stubs for incomplete functions enables
testing of the flow of control among the other
functions before these function are completed.
• The process of testing the flow control between
a main and subordinate functions is called topdown testing.
Stup for Function multiply_fractions
/*
***** STUB *****
* Multiplies fractions represented by pairs of integers.
* Product of n1/d1 and n2/d2 is stored in variables pointed
* to by n_ansp and d_ansp. Result is not reduced.
*/
void
multiply_fractions(int n1, int d1, /* input - first fraction */
int n2, int d2, /* input - second fraction */
int *n_ansp,
/* output */
int *d_ansp)
/* product of two fractions */
{
/* Displays trace message
*/
printf("\nEntering multiply fractions with\n");
printf("n1 = %d, d1 = %d, n2 = %d, d2 = %d\n", n1, d1, n2, d2);
/* Defines output arguments
*/
*n_ansp = 1;
*d_ansp = 1;
}
Testing
• The stub functions can be completed and test
individually. A test of an individual function is
referred to as unit test.
• When a module is completed and tested, it can
be substituted for its stub in the program.
• Bottom-up testing is the process of testing
individual functions of a program.
• Testing the entire program after replacing all its
stubs with the pre-tested functions is referred to
as system integration test.
Common Programming Errors
• The correspondence between actual parameters
and formal parameters (number, type, order)
• Actual output arguments must be of the same
pointer types as the corresponding formal
parameters.
• Recursive functions should be tested to make
sure they terminate properly
• If return is using for the expression containing
the call to the recursive function, every path
through a non-void function should lead to a
return statement.