Download as pdf or txt
Download as pdf or txt
You are on page 1of 17

Coventry University

Facilty of Engineering & Computing

Black Box model of a function

From none to indefinite number of inputs

foo

return either none or one result

Single-Result functions
The standard math library functions are examples of functions that take from 1 to up to 3 input parameters and return a single result of either int, double or long double data type. Single-result functions can be used anywhere that a value of the return type can be used. e.g. #include <math.h> void main(void) { : : if (sqrt(pow(x1 - x2, 2) - pow(y1 - y2, 2))) > 100) : : sinX = sin(x); : printf("cos(30) = %f", cos(30)); : }

Basic C\Functions

2/17

RJR\Dec98\Functions.docx

Coventry University

Facilty of Engineering & Computing

// Program with User Defined Function to Compute MPG #include <stdio.h> int calc_mpg ( double oldOdom, double newOdom, double gallons ); int main() { double oldMiles, newMiles, gallons; int mpg; printf("Previous odometer reading > "); scanf("%lf", &oldMiles); printf("New odometer reading > "); scanf("%lf", &newMiles); printf("Gallons added to fill tank > "); scanf("%lf", &gallons); mpg = calc_mpg(oldMiles,newMiles,gallons); printf("Car got %4d mpg on last fill-up.", mpg); } return 0; // end of function main a tank of fuel oldOdom, double newOdom, gallons )

// Calculate mpg for int calc_mpg ( double double { : : : } // end of function

calc_mpg

Basic C\Functions

3/17

RJR\Dec98\Functions.docx

Coventry University

Facilty of Engineering & Computing

// Definition of a function Definition | Calculate miles per gallon for a tank of fuel | Pre: oldOdom, newOdom, and gallons are nonnegative; | newOdom >= oldOdom | | Header | int calc_mpg ( double oldOdom, // old odometer reading | | double newOdom, // new odometer reading | | double gallons ) | | { Body | int ans = (int)((newOdom - oldOdom) / gallons + 0.5); | | | | return ans; | | | | // or return (newOdom - oldOdom) / gallons + 0.5 | | | | } // end of function calc_mpg | | // // // //

Black box model of function mpg

double

oldOdom int return

double

newOdom

calc_mpg

double gallons

Basic C\Functions

4/17

RJR\Dec98\Functions.docx

Coventry University

Facilty of Engineering & Computing

MPG = mpg(oldMiles, newMiles, gallons); 33567.8 33808.3 10.7

returns result value of 22

//

Calculate miles per gallon for a tank of fuel

int mpg (double oldOdom, double newOdom, double gallons) {// 33567.8 33808.3 10.7 return (int)((newOdom - oldOdom) / gallons + 0.5); } // end of function mpg

(a) Memory before execution ? ? ? ? ? ? ? ? ? + Data area | for main | + oldMile s newMile s gallons MPG

(b) After OS calls main & main gets data 33567.8 33808.3 10.7 ? ? ? ? ? ?

Basic C\Functions

5/17

RJR\Dec98\Functions.docx

Coventry University

Facilty of Engineering & Computing

(c) After main calls mpg + Data area | for main | + + Data area | for mpg | + oldMiles newMiles gallons MPG oldOdom newOdom gallons ans ? 33567.8 33808.3 10.7 ? 33567.8 33808.3 10.7 + Data area | for main | + oldMiles newMiles gallons MPG

(d) After mpg returns 33567.8 33808.3 10.7 22 33567.8 33808.3 10.7 22 ?

Basic C\Functions

6/17

RJR\Dec98\Functions.docx

Coventry University

Facilty of Engineering & Computing

Black Box model of a function with input and output parameters


From none to indefinite number of parameters

foo

return either none or one result

Black Box model of addVect function


double double double& double& a void return b rMagnitude rDirection

AddVect

Definition of addVect function


// Add vectors a and b that are at right angles: a is horizontal, // b is vertical, a > 0, b > 0. Express direction of // resultant in whole degrees. void addVect( double a, double b, double *rMagnitude, int *rDirection ) { double rDirRadians; // direction of resultant in radians *rMagnitude = sqrt( a * a + b * b ); *rDirRadians = atan( b / a ); *rDirection = int((180 / Pi * rDirRadians + 0.5)); // NB cast return; } // optional // i/p - vectors @ rt angles // o/p - mag of resultant // o/p - dirn. of resultant // in whole degrees

Basic C\Functions

7/17

RJR\Dec98\Functions.docx

Coventry University

Facilty of Engineering & Computing

// // //

Vector Addition Program Add two vectors that are at right angles to each other.

#include <stdio.h> #include <math.h> const double Pi = acos(-1); void addVect(double, double, double *, int *); void main(void) { double aVect, bVect, rMag; int rDir; printf("Enter mag of horz vector A "); scanf("%lf", &aVect); printf("Enter mag of vert vector B "); scanf("%lf", &bVect); // N.B. Call to addVect must stand alone addVect( aVect, bVect, &rMag, &rDir); printf("A + B yields vector of mag %f & dir %d degrees\n", rMag, rDir); return; } // Note change to void

Basic C\Functions

8/17

RJR\Dec98\Functions.docx

Coventry University

School of Engineering

(a) Memory as addVect is invoked + Data area | for main | + + Data area | for addVect | | + aVect bVect rMag rDir a b rMagnitude rDirection rDirRadians 3.0 4.0 ? ? 3.0 4.0 63976 63974 ? ? ?

Memory address 63992 63984 63976 63974 63966 63958 63956 63954 63948

Space (bytes) 8 8 8 2 8 8 2 2 8

Kinds of Functions
Purpose Return Type Same as type to be computed or obtained Parameters Notes

To compute or obtain as input a single numeric or character value.

I/Ps hold copies of data passed by caller.

Function code includes a return statement with an expression whose value is the result.

To display output containing values of arguments.

void int bool

I/Ps hold copies of data passed by caller.

Can return int or bool to signify success. Results are stored in caller's data area by assign to ref parameters. Can return int or bool to signify success.

To compute multiple results.

void int bool

I/Ps hold copies of data passed by caller. O/Ps are references to actual arguments.

To modify argument values.

void int bool

I/O parameters are references to actual arguments.

As above.

Basic C++\Functions 10/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

Coventry University

School of Engineering

Storage Classes and Scope Rules


Once we start using functions which break a program into self-contained blocks, it becomes important to understand what sections of a program can access each variable and what that variable's lifetime is. Variables can be classified by the position of their declaration: local or global and by their storage class. ANSI C++ provides four storage classes :auto static register extern

Local variables are declared at the start or within the body of a block; they can be used only by code in that block and (unless declared as static) exist only while the block is active. Global variables are declared outside any block; they can be used by any code that follows their declaration and they exist for the whole life of the program. By default, they are of class extern. Most programs are written with local variables of the auto class. While global variables are very useful, they are also a source of unexpected 'side effect' errors because they can be written to by more than one function. They must therefore only be used in a very disciplined way, such as allowing only one function to write to them but allowing other functions to read from them. Use them only with good reason, such as avoiding overburdening the parameter lists of functions with input data.

Basic C++\Functions 11/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

Coventry University

School of Engineering

Program Outline for Study of Scope Rules const int Bound = 100; double x; int p( double ); int k( int ); int main() { double m; int x; for (x = 0; x < Bound; ++x) { double z; ... } ... } int p( double k ) { int i; ... } int k( int m ) { ... } // global constant // global variable

Basic C++\Functions 12/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

Coventry University

School of Engineering

automatic variables : are the default are private or local to the block in which they are declared and any inner block. exist only for the life of the block. are not automatically initialised. e.g. variables i and x in the cube function example are auto variables.

static variables : have permanent storage for the life of the program. are private to the function in which they are declared. are initialised once only and to zero at load time. on leaving a function, the value is not lost. on re-entering a function, the value is not reinitialised. /* Example */ #include <stdio.h> void displayx ( void ); main() { int i for (i = 0; i < 5; i++) { printf("i = %d"); displayx(); } } /* end of main */ void displayx ( void ) { static int n; /* init to 0 at load time */ printf("%d\n", n++); } /* end of displayx */

What does this program output?

Basic C++\Functions 13/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

Coventry University

School of Engineering

register variables : the declaration register int i = 1; is a suggestion to the compiler to place i in one of the computer's high speed hardware registers. the compiler may ignore the request if a register is not available. can only be used with automatic variables. is normally only used in intensive calculations where speed is important.

external variables : variables declared outside any block are extern by default and known globally to all functions declared thereafter. a declaration such as extern int n; (which is a referencing declaration) is only used if :n is globally declared in another compilation module n has been declared after the current block. are initialised once only and to zero at load time.

Basic C++\Functions 14/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

Coventry University

School of Engineering

Recursion and its Dangers


A recursive function is one which calls itself. Recursive functions may be used where the solution to a problem is inherently recursive, i.e. the problem appears in the solution. A classical example is calculating a factorial number. The solution may be expressed recursively as :0! = 1 (by definition) 1! = 1 n! = n * (n-1)! (factorial appears on both sides of the equation) A recursive function can be written directly from this expression as e.g. :long factorial(int n) { if (n <= 1) return 1L; return ((long)n * factorial(n - 1)); } For values of n > 1, repeated calls are made to factorial(n - 1) which queue up on the stack until, eventually, n is reduced to 2 and a call is made to factorial(1). This is the base case and factorial() is able to return 1 to the previous call. This triggers a "cascade" of returns resulting in a value for n!. The recursive solution is very elegant and very easy to code. We rarely even need to visualise how it is working. (For the "Towers of Hanoi" problem such visualisation is virtually impossible.) When the values of n are relatively small, there a no problems with using the recursive solution. For very large values of n however there are two possible problems :1. The program runs slowly because of the amount of push and pop operations on the stack. 2. The stack could grow so large with return addresses and auto variables that it crashes into the program area. The iterative solution suffers neither of these problems and for the factorial problem is also easy to code :-

Basic C++\Functions 15/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

Coventry University

School of Engineering

long factorial(int n) { long i, factnum = 1L; if (n <= 1) return 1L; for(i = 1; i < = n; i++) factnum *= i; return factnum; }

The rule of thumb is therefore to use an iterative solution if one exists. This is dramatically illustrated by the fibonacci series which has the recursive definition : f(0) = 0; f(1) = 1; f(n) = f(n-1) + f(n-2); If this is programmed recursively then for n > 1, the recursive step generates two further recursive calls. To simply calculate the 20th number would require of the order of 22000 recursive calls. Similarly the 30th number requires of the order of 2.7 million recursive calls. By about the 40th number, it is quicker to calculate by hand! (As an aside, it turns out that the number of recursive calls for the fibonacci series is also a fibonacci series, i.e. :fcalls(0) = 1; fcalls(1) = 1; fcalls(n) = fcalls(n-1) + fcalls(n-2) + 1;) Again, the iterative solution is straightforward and direct :// Iterative function for fibonacci series // Pre : n >= 0 long fib(int n) { long fn_minus_2 = 1, fn_minus_1 = 0, fn; if (n <= 1) return (long)n; for (int i = 1; i <= n; i++) { fn = fn_minus_1 + fn_minus_2; fn_minus_2 = fn_minus_1; fn_minus_1 = fn; } return fn; }

Basic C++\Functions 16/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

Coventry University

School of Engineering

The C++ Preprocessor and Macros


The C++ preprocessor supports a text replacement facility. All preprocessor directives begin with the # symbol. We have already made use of this facility with the #include <stdio.h> directive. This causes a copy of the file stdio.h to be included in place of the directive. Another common directive is #define. This can be used to create :-

symbolic constants, eg. #define SIZE 500 Every occurrence of SIZE in the program will be replaced by 500. Note: by convention the item to be replaced (e.g. SIZE) is written in upper case When reading the program, upper case items can be expected to be replaced. object-like macros, eg. #define R_U_SURE "Are you sure?" Using cout << R_U_SURE; will give a printout of: Are you sure?

function-like macros /* Example */ #define PRINT(x) printf("x = %d", x) main() { int i = 5; PRINT(i); } Note : ANSI C does not perform replacement within quotes.

Basic C++\Functions 17/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

Coventry University

School of Engineering

Macros and Side-Effects


Macros with arguments look very much like functions and do not have the associated function call overheads. BUT there are potential pitfalls, e.g. :#define SQR(x) x * x main() { int n = 5; printf("Square(&d) = %d\n", n, SQR(n)); } For a simple case e.g. :SQR(5) = 25 but for a case such as :SQR(n+2) = 17 when n = 5 there are problems. Why? there are no problems

#define SQR(x) (x) * (x) cures this but :- 100/SQR(n+3) still fails. #define SQR(x) ((x) * (x)) cures this but even this can fail in certain circumstances.

Summary of Functions v. Macros


Object Function For Executable code appears only once No side effects No call overhead No type checking Against Call overhead

Macro

Possible side effects Text replacement on every occurrence

Basic C++\Functions 18/17RJR\Dec98\C:\Documents and Settings\cex001\My Documents\Courses\Basic_C\Lecture_

You might also like