8/7/2018

C/++ Programming for Engineers: Functions

John T. Bell

Department of Computer Science University of Illinois, Chicago

Review

Suppose a C++ array "data" was originally declared to have 10 elements as follows: int nElements = 10; double data[ nElements ];

How can the array size later be increased to 20 elements?

A. By changing the variable nElements to 20. B. By redeclaring data as "double data[ 20 ];" C. By storing a number in the 20th position of data[ ]. D. Error. The above code is invalid to begin with, and won't compile. E. Impossible - There is nothing wrong with the code shown, but there is also no way to change the size of an array in C++ once it has been allocated space.

2

1 8/7/2018

Motivations for Writing Functions

• Reuse code, without cutting and pasting. • Reduce errors. Test functions thoroughly, in all situations, then trust them to work right. • Incremental / Modular Development. Develop and test one piece at a time. • Improve readability, pulling code out of main. • Flexibility, through passed parameters. • Build libraries, e.g. sqrt( ), etc.

3

General Concept of Functions

1. When a function call is made, control transfers from the point of the call into the function. – If the function takes parameters, then information is passed from the calling code to the function. 2. The function executes, with associated effects. 3. When the function completes, it returns control back to the code that called it. – If the function returns a value, then that information is passed back to the calling code.

4

2 8/7/2018

General Syntax of a Function return_type function_name( arg_type argument, ... ) { local_variable_type local_variables; executable statement(s); return return_value; }

5

Simple Sample Function

int add( int a, int b ) { int sum; sum = a + b; return sum; }

6

3 8/7/2018

Return Type

• The “” indicates what kind of data this function returns, if any. Example: int. • Keyword “void” expressly states that this function does not return any value. ( Some languages would call this a “”. ) • If the return type is omitted, some may assume some type. This is a bad idea.

7

Function Name

• The function name is the name by which this function may be called. • Standard variable naming rules apply: – Begin with a letter. Continue with letters, numbers, or underscores only. – No two items ( in the same ) may have the same name. Don’t name functions and variables with the same names.

8

4 8/7/2018

Formal Parameter List ( arg_type argument, . . . )

• This lists the number of and types of data that are passed in to the function, and the variable names used within the function to received the passed information. • Each argument must have its type specified. • Keyword “void” indicates the function takes no input arguments. – Leaving the parentheses blank makes the assume things. This is a bad idea.

9

Function Body

• The main body of the function is contained within a pair of { curly braces }, which defines the function’s scope. • Any variables declared within the scope of a function, ( including the function arguments ), only exist within that scope, and cease to exist when the function returns. – There is no relationship between variables declared within one scope and variables with the same names declared in other scopes. ( More on scope later. )

10

5 8/7/2018

Return statement

• Execution of a stops execution of a function, and returns control back to the code that called the function. • Only one return statement may be executed. • If the function returns a value, then exactly one item of the appropriate type follows the return keyword. E.g. “return 42;” or “return x;” • If the function is declared with a void return type, then no value is returned. E.g. “return;”

11

Preview

Supposing a C++ function needs to send multiple values back to the function that called it. How can this be done? A. Using a single return statement with multiple values. B. Using multiple return statements. C. Using pass-by-reference parameters. D. Using an array of values, as long as all the values being sent back are all of the same . E. Either C or D would work.

12

6 8/7/2018

Calling Functions - Actual versus Formal Parameters

• The variable names assigned to function parameters within the function are termed “formal” parameters. • The items passed by code when calling a function, whether variables or constants, are termed “actual” parameters.

13

Scope

• “Scope” defines the range during which a variable exists and is accessible. • There are four types of scope to know: – Local variables – Function arguments ( a.k.a. formal parameters ) – GLOBAL variables – Very local variables

14

7 8/7/2018

The Scope of Local Variables

• Local variables are declared inside of a function. • In C they had to be declared before any executable statements; In C++ they may be declared any time before they are used. • The scope extends from the point of declaration to the end of the function. • Local variables are not automatically initialized.

15

The Scope of Function Arguments

• Function arguments (a.k.a. formal parameters), are exactly the same as other local function variables, except: • Formal parameters are initialized with values passed to the function when the function is called. • The scope of formal parameters is the entire duration of the function.

16

8 8/7/2018

The Scope of Global Variables

• Global variables are declared outside of any function, usually at the beginning of a file. • The scope of a global variable is from the point of declaration to the end of the file. They are accessible by ALL functions within that range. • Global variables tend to cause lots of hard-to-find errors. They should never be used unless const or absolutely necessary. ( I.e. only consts in CS 109. ) • By convention, globals are in ALL CAPS (for humans. )

17

The Scope of Very Local Variables

• Very local variables are defined within a set of { curly braces }, such as within a loop, switch, or if-else construct. • Their scope extends from the point of declaration to the closing brace of their scope. • In this common implementation, “i” only exists within the for loop, and goes out of scope when the loop exits: for( int i = 0; i < nSteps; i++ ) {

18

9 8/7/2018

Eclipsing

• When the same variable name is used in overlapping scopes, the variable with the more specific scope “eclipses” or hides the variable with the same name in the more general scope. A few examples: – A local variable will eclipse a global variable. – A very local variable will eclipse a local variable. – A local variable will eclipse a formal parameter*. • Note that the eclipsed variables continue to exist, and “reappear” when the eclipsing variable goes out of scope.

* - If the compiler even allows this at all. 19

Function Declarations () versus Function Definitions

• A function declaration informs the compiler of the existence of a function, its return type, and its parameter types, but does not define the function body. • A function definition defines the body of a function, possibly one that was previously declared but not yet defined. A function may be defined when declared. • Functions must be declared before they can be called, but they can be defined later. • The scope of function declarations parallels those of variables. They are normally declared globally at the beginning of files. ( e.g. #includes. See later slide. )

20

10 8/7/2018

Function Prototypes

• A function declaration made without its definition is known as a function . • Prototypes improve readability, by allowing main( ) to appear before the functions it calls. • Parameter names are allowed in prototypes, and are good for humans, but are ignored by the compiler: – double delta( double max, double min, int nSteps); – double delta( double, double, int ); • Note that function prototypes end in a semicolon;

21

#include and Prototypes

• “#include” says “Go find this other file and copy its contents into this place of the code.” • #included files often contain function prototypes, e.g. of math functions in . • #included files in are searched for in standard system directories. • #included files in “quotation marks” are searched for in the local directory, or sometimes in specified directories.

22

11 8/7/2018

Specifying Data Types in Parameters

• Data types must be specified in function prototypes and definitions, but they are not included when calling the function. • Example prototype: ▪ double sqrt( double ); • Example function call: ▪ answer = sqrt( X ); // Note no “double” here.

23

Calling Functions - Parameters are passed by position, not by name.

• The 1st actual parameter passed is used to initialize the 1st formal parameter in the function; the 2nd actual initializes 2nd formal, … • If the same variable name happens to be used in both calling code and called function, that is purely a coincidence. There is no relationship or connection between variables in different scopes based on their shared names. • Actual parameters can be reused. Formals not.

24

12 8/7/2018

Parameter Passing I - By Value

• When an ordinary data type ( e.g. int, double ), is passed to a function, it is normally passed by value. • This means that only a number is sent from the calling code, which is used to initialize the formal parameter within the function. • Any changes made to the formal parameter remain local to the function, and have no impact on the calling code. ( What happens in functions stays in functions. ) • ( The variables passed by the calling code are termed the “actual” parameters. )

25

Example of Pass By Value

double func( double x, double z, double b, double c ); // prototype int main( void ) {

double x( 1.0 ), y( 2.0 ), z( 3.0 );

z = func( x, y, z, x );

cout << « z = « << z << endl; 1.0 2.0 3.0 1.0 double func( double x, double z, double b, double c ) { return 0; } z += 2; // Affects local z only, not y or z in main return x + z * b / c; }

26

13 8/7/2018

Review

How many asterisks would be printed by the following code? for( int i = 0; i < 42; i += 2 ) { int j = 0; do { cout << '*'; } while( ++j <= 0 ); cout << endl; } A. None. B. 21 C. 42 D. 84 E. 422

27

Parameter Passing II - By Reference

• An alternative parameter passing mechanism, indicated by an “&” after the data type: – void swap( int & a, int & b ); • Under pass-by-reference, the formal parameter becomes an alias for the actual parameter. – Changes made to the parameter in the function DO affect the passed variable in the calling code.

28

14 8/7/2018

Pass By Reference Swapping Example

WRONG RIGHT void swap( int a, int b ) { void swap( int & a, int & b ) { int temp = a; int temp = a; a = b; a = b; b = temp; b = temp; return; return; } } // Changes local to function // Changes perform swap

29

Pass By Reference Effectively “Returns” Multiple Results:

• double root( double x, bool & imaginary ) { imaginary = x < 0.0; return sqrt( abs( x ) ); } • int polar2Rect( double r, double theta, double & x, double & y ); // “Return” value is an error code, e.g. r<0

30

15 8/7/2018

Passing Large Items by (const) Reference

• Large items, e.g. string objects, require overhead to pass by value. • These are often passed by reference for efficiency reasons. • The function can be prohibited from changing variables that are passed by reference by declaring them as “const”.

31

Example Passing String by Reference

double getDouble( const string & prompt ) { double result; cout << prompt; cin >> result; return result; } // ( Could be enhanced with error checking. )

32

16 8/7/2018

Parameter Passing III - Arrays

• When an entire array is passed to a function, it is ( effectively ) passed by reference. • In this case the array name is passed without any [ square brackets ] • Any changes made to the array contents within a function DO affect the array contents in the code that called the function. – Unless the array is declared as a const parameter.

33

Specifying Array Types in Functions

• The 1st dimension is ignored; should be omitted. • All other dimensions must be specified, and must match between calling and called code. • Sizes are usually passed as additional args: – int func( double data[ ], int nData ); – const int NCOLS = 40; – double func2( double x[ ][ NCOLS ], int nRows, int nCols ); // Only use first nCols, <= NCOLS

34

17 8/7/2018

Sample Function Using Arrays double sumArray( double data[ ], int nData ) { double sum = 0.0; for( int i = 0; i < nData; i++ ) sum += data[ i ]; return sum; } // Returns 0.0 if nData <= 0. Could be better.

35

Sample Calling Function Using Arrays double sumArray( double data[ ], int nData ); int main( void ) { double x[ 100 ] = { 3.14, 6.28, 9.42, 12.56 }; cout << “The sum of i * pi for i = 1 to 4 is “ << sumArray( x, 4 ) << endl; return 0; } // Note that nData is # of values to process.

36

18 8/7/2018

Review

When writing a function in C++ that takes an array as an argument, which dimensions must be specified? A. All of them. B. Only the first. C. All except the first. D. Only the last. E. All except the last.

37

Preview

Which preprocessor directive is used to ( effectively ) copy the contents of one file into another? A. #ifndef B. #define C. #endif D. All of the above. E. #include

38

19 8/7/2018

Default Parameter Values

• It is possible to assign default values to some function parameters, starting with the last parameter and working backwards. • When calling such functions, parameters may be left off from the end, provided defaults are given, but they may not be skipped. • If calling code does provide parameters, they override the defaults given.

39

Sample Code Using Defaults

• double area( double side, double side2 = -1.0){ if( side2 <0.0 ) return side * side; return side * side2; }

• rectangleArea = area( 10.0, 20.0 ); // 200.0 • squareArea = area( 5.0 ); // 25.0

40

20 8/7/2018

Function Overloading

• C required every function to have a unique name, e.g. squareInt( int );, squareFloat( float );, etc. • C++ considers the function signature when distinguishing between functions. • The signature includes the number and types of arguments, but not the return type. E.g.: a) double square( double, double ); b) double square( double ); Recognized as unique. OK c) int square( int ); d) double square( int ); Appears identical - Conflict

41

Beware of Ambiguities Involving Function Overloading

• With automatic type conversions: – double square( double ); – float square( float ); – result = square( 5 ); // Which function is called? • With defaults – double square( double side1, double side2 = -1.0 ); – double square( double ); – result = square( 42.0 ); // Which function is called?

42

21 8/7/2018

FYI - Operator Overloading

• Just FYI, C++ also allows programmers to overload operators, e.g. for new types. • For example, you could define the + operator for adding a string plus an int, maybe like: string s = “Billy is “; s = s + 4; cout << s << “.\n”; // prints “Billy is four.”

43

Unit Testing

• One of the main benefits of writing functions is that they can be tested independently. • This requires writing test drivers, and/or test stubs: – Test drivers are main( ) programs, whose job is to call functions with test inputs and report results. – Test stubs are temporary stand-ins for functions not yet written, returning simple results. (e.g. “true”)

44

22 8/7/2018

Selecting Test Input

• Goal is to reveal flaws, not show correct runs. • Consider boundaries, e.g. for valid range, and select inputs just each side, e.g. Feb 28, 29, 30. • Consider very large or very small values, e.g. 1.0E20, -3.14E-25 • Consider unusual values, likely to identify flaws. ( X = 1.0 will not distinguish between X * X and X * X * X ) • Play “I bet you can’t break my code!” w. a friend.

45

Test Drivers, a.k.a. Test Harnesses

• Should report test input, output, and expected results. (“Oracle” determines expected results.) • Inputs may be hard-coded, ( as illustrated in Zyante ), or read in from the user, or generated dynamically ( randomly or from an algorithm. ) • In practice test input and output are frequently read in from a file and/or written to files. – “diff” compares two files for differences.

46

23 8/7/2018

Preprocessor

• When a C/C++ program is compiled, the first step is to run the code through a preprocessor. • The preprocessor looks for directives, indicated by a # at the beginning of a line. • The preprocessor modifies the code based on instructions given in the directives. • E.g. #include says to go copy another file into this location of ( a copy of ) the code.

47

Common Preprocessor Directives

• #include - Copy a file here. • #define - Used for substitution, logic, macros – #define MAXCOLS 42 • ( A better approach today is const int MAXCOLS = 42; ) – #define DEBUG – #define SQUARE(X) ((X)*(X)) • #ifdef, #ifndef, #elif, #else, #endif - Used to optionally include code based on whether or not certain things are defined.

48

24 8/7/2018

Multi-File Development

• It is often beneficial to place (related) function(s) into (a) separate file(s). • This allows the same functions to be used in multiple programs without having to cut-and- paste the code. • It also means the functions do not have to be recompiled every time, and the source code does not have to be shared.

49

Header Files

• When functions are placed in separate files, main() still needs to see the function prototypes. • This is usually done by putting the prototypes in header files, with a .h file extension. • The header files are then #included in the main program file, the function file, and any other files that need to know about the prototypes. • DO NOT PUT CODE IN HEADER FILES. – Some #defines are okay, e.g. for defined constants.

50

25 8/7/2018

Header File Standard Procedures

• User-written header files should have a .h file extension. – C++ standard header files have no extension. • #include looks for file in standard directories. #include “file” looks here. • C++ header files that derived from C header files begin with the letter c, e.g. cmath, cstdlib. • Headers should contain prototypes and defines only. No actual function definitions ( code ).

51

Avoiding Circular Includes

• Header files are often #included, and often #include other header files themselves, potentially leading to circular includes. • Avoid this, e.g. in file myHeaders.h with: #ifndef MYHEADERS_H #define MYHEADERS_H // rest of function prototypes, etc. #endif

52

26 8/7/2018

Summary of Multi File Development

? ? ?

53

Review

Which preprocessor directive is used to prevent circular includes? A. #ifndef B. #define C. #endif D. All of the above. E. #include

54

27