7 7 quiz function call stack passing values to functions recursion
Post on 19-Dec-2015
241 views
TRANSCRIPT
77 QuizFunction call stackPassing values to
functionsRecursion
Quiz1. Create a global two dimensional int array of size two
rows 10 columns
– Initialize the first row to [88,10,20,7,54,33,21,77,9,3]
– Initialize the second row to 0
– Name the array actual
2. Create a separate global int array answer of size 10
Quiz Look only at row zero for the time being.
Create a function lessthan that takes an element of the actual array as input and returns the number of items in row 0 of actual array lower than that element. For instance, if the input is 10, the function
would return 3, if the input is 9, the function would return 2, 88 would answer 9
The way to pass elements of an array to the function is • lessthan(array1[1][3])
Store this information in row 1 of the actual array
BY the end of this step, the actual would look like this
88 10 20 7 54 33 21 77 9 3
9 3 4 1 7 6 5 8 2 0
Quiz
• Create the sorted copy of the initial array using this information and store it in answer
– Row 1 now contains the index of a sorted array
– Corresponding values are stored in row 0
– answer[index]=value;
88 10 20 7 54 33 21 77 9 3
9 3 4 1 7 6 5 8 2 0
Actual
Sorted copy3 7 9 10 20 21 33 54 77 88
Case Study: Game of Chance and Introducing enum
• Enumeration– A set of integer constants represented by identifiers
• The values of enumeration constants start at 0, unless specified otherwise, and increment by 1
• The identifiers in an enum must be unique, but separate enumeration constants can have the same integer value
– Defining an enumeration• Keyword enum • A type name• Comma-separated list of identifier names enclosed in braces• Example
– enum Months { JAN = 1, FEB, MAR, APR };
Craps
• A player rolls two dice. Each die has six faces. These faces contain 1, 2, 3, 4, 5 and 6 spots. After the dice have come to rest, the sum of the spots on the two upward faces is calculated. If the sum is 7 or 11 on the first roll, the player wins. If the sum is 2, 3 or 12 on the first roll (called "craps"), the player loses (i.e., the "house" wins). If the sum is 4, 5, 6, 8, 9 or 10 on the first roll, then that sum becomes the player's "point." To win, you must continue rolling the dice until you "make your point." The player loses by rolling a 7 before making the point.
1 // Fig. 6.11: fig06_11.cpp
2 // Craps simulation.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include <cstdlib> // contains prototypes for functions srand and rand
8 using std::rand;
9 using std::srand;
10
11 #include <ctime> // contains prototype for function time
12 using std::time;
13
14 int rollDice(); // rolls dice, calculates amd displays sum
15
16 int main()
17 {
18 // enumeration with constants that represent the game status
19 enum Status { CONTINUE, WON, LOST }; // all caps in constants
20
21 int myPoint; // point if no win or loss on first roll
22 Status gameStatus; // can contain CONTINUE, WON or LOST
23
24 // randomize random number generator using current time
25 srand( time( 0 ) );
26
27 int sumOfDice = rollDice(); // first roll of the dice
Outline
•fig06_11.cpp
•(1 of 4)#include and using for function time
Enumeration to keep track of the game status
Declaring a variable of the user-defined enumeration type
Seeding the random number generator with the current time
28
29 // determine game status and point (if needed) based on first roll
30 switch ( sumOfDice )
31 {
32 case 7: // win with 7 on first roll
33 case 11: // win with 11 on first roll
34 gameStatus = WON;
35 break;
36 case 2: // lose with 2 on first roll
37 case 3: // lose with 3 on first roll
38 case 12: // lose with 12 on first roll
39 gameStatus = LOST;
40 break;
41 default: // did not win or lose, so remember point
42 gameStatus = CONTINUE; // game is not over
43 myPoint = sumOfDice; // remember the point
44 cout << "Point is " << myPoint << endl;
45 break; // optional at end of switch
46 } // end switch
47
48 // while game is not complete
49 while ( gameStatus == CONTINUE ) // not WON or LOST
50 {
51 sumOfDice = rollDice(); // roll dice again
52
Outline
•fig06_11.cpp
•(2 of 4)
Assigning an enumeration constant to gameStatus
Comparing a variable of an enumeration type to an enumeration constant
53 // determine game status
54 if ( sumOfDice == myPoint ) // win by making point
55 gameStatus = WON;
56 else
57 if ( sumOfDice == 7 ) // lose by rolling 7 before point
58 gameStatus = LOST;
59 } // end while
60
61 // display won or lost message
62 if ( gameStatus == WON )
63 cout << "Player wins" << endl;
64 else
65 cout << "Player loses" << endl;
66
67 return 0; // indicates successful termination
68 } // end main
69
70 // roll dice, calculate sum and display results
71 int rollDice()
72 {
73 // pick random die values
74 int die1 = 1 + rand() % 6; // first die roll
75 int die2 = 1 + rand() % 6; // second die roll
76
77 int sum = die1 + die2; // compute sum of die values
Outline
•fig06_11.cpp
•(3 of 4)
Function that performs the task of rolling the dice
78
79 // display results of this roll
80 cout << "Player rolled " << die1 << " + " << die2
81 << " = " << sum << endl;
82 return sum; // end function rollDice
83 } // end function rollDice Player rolled 2 + 5 = 7 Player wins Player rolled 6 + 6 = 12 Player loses Player rolled 3 + 3 = 6 Point is 6 Player rolled 5 + 3 = 8 Player rolled 4 + 5 = 9 Player rolled 2 + 1 = 3 Player rolled 1 + 5 = 6 Player wins Player rolled 1 + 3 = 4 Point is 4 Player rolled 4 + 6 = 10 Player rolled 2 + 4 = 6 Player rolled 6 + 4 = 10 Player rolled 2 + 3 = 5 Player rolled 2 + 4 = 6 Player rolled 1 + 1 = 2 Player rolled 4 + 4 = 8 Player rolled 4 + 3 = 7 Player loses
Outline
•fig06_11.cpp
•(4 of 4)
Good Programming Practice
• Capitalize the first letter of an identifier used as a user-defined type name.
Function Call Stack and Activation Records
• Data structure: collection of related data items
• Stack data structure– Analogous to a pile of dishes
– When a dish is placed on the pile, it is normally placed at the top
• Referred to as pushing the dish onto the stack
– Similarly, when a dish is removed from the pile, it is normally removed from the top
• Referred to as popping the dish off the stack
– A last-in, first-out (LIFO) data structure• The last item pushed (inserted) on the stack is the first item
popped (removed) from the stack
Function Call Stack and Activation Records (Cont.)
• Function Call Stack– Sometimes called the program execution stack
– Supports the function call/return mechanism• Each time a function calls another function, a stack frame
(also known as an activation record) is pushed onto the stack
– Maintains the return address that the called function needs to return to the calling function
– Contains automatic variables—parameters and any local variables the function declares
Function Call Stack and Activation Records (Cont.)
• Function Call Stack (Cont.)– When the called function returns
• Stack frame for the function call is popped• Control transfers to the return address in the popped stack frame
– If a function makes a call to another function• Stack frame for the new function call is simply pushed onto the call
stack• Return address required by the newly called function to return to its
caller is now located at the top of the stack.
• Stack overflow – Error that occurs when more function calls occur than can have
their activation records stored on the function call stack (due to memory limitations)
1 // Fig. 6.13: fig06_13.cpp
2 // square function used to demonstrate the function
3 // call stack and activation records.
4 #include <iostream>
5 using std::cin;
6 using std::cout;
7 using std::endl;
8
9 int square( int ); // prototype for function square
10
11 int main()
12 {
13 int a = 10; // value to square (local automatic variable in main)
14
15 cout << a << " squared: " << square( a ) << endl; // display a squared
16 return 0; // indicate successful termination
17 } // end main
18
19 // returns the square of an integer
20 int square( int x ) // x is a local variable
21 {
22 return x * x; // calculate square and return result
23 } // end function square 10 squared: 100
Outline
•fig06_13.cpp
•(1 of 1)Calling function square
Fig. 6.14 | Function call stack after the operating system invokes main to execute the application.
Operating system calls main, pushing an activation record onto the stack
Fig. 6.15 |
Function call stack after main invokes function square to perform the calculation.
main calls function square, pushing another stack frame onto the function call stack
Fig. 6.16 | Function call stack after function square returns to main.
Program control returns to main and square’s stack frame is popped off
Inline Functions
• Inline functions – Reduce function call execution-time overhead—especially
for small functions
– Qualifier inline before a function’s return type in the function definition
• “Advises” the compiler to generate a copy of the function’s code in place (when appropriate) to avoid a function call
– Trade-off of inline functions• Multiple copies of the function code are inserted in the
program (often making the program larger)
– The compiler can ignore the inline qualifier and typically does so for all but the smallest functions
Good Programming Practice
• The inline qualifier should be used only with small, frequently used functions.
Performance Tip
• Using inline functions can reduce execution time but may increase program size.
1 // Fig. 6.18: fig06_18.cpp
2 // Using an inline function to calculate the volume of a cube.
3 #include <iostream>
4 using std::cout;
5 using std::cin;
6 using std::endl;
7
8 // Definition of inline function cube. Definition of function appears
9 // before function is called, so a function prototype is not required.
10 // First line of function definition acts as the prototype.
11 inline double cube( const double side )
12 {
13 return side * side * side; // calculate cube
14 } // end function cube
15
16 int main()
17 {
18 double sideValue; // stores value entered by user
19 cout << "Enter the side length of your cube: ";
20 cin >> sideValue; // read value from user
21
22 // calculate cube of sideValue and display result
23 cout << "Volume of cube with side "
24 << sideValue << " is " << cube( sideValue ) << endl;
25 return 0; // indicates successful termination
26 } // end main Enter the side length of your cube: 3.5 Volume of cube with side 3.5 is 42.875
Outline
•fig06_18.cpp
•(1 of 1)Complete function definition so the compiler knows how to expand a cube
function call into its inlined code. inline qualifier
cube function call that could be inlined
References and Reference Parameters
• Two ways to pass arguments to functions– Pass-by-value
• A copy of the argument’s value is passed to the called function
• Changes to the copy do not affect the original variable’s value in the caller
– Prevents accidental side effects of functions
– Pass-by-reference• Gives called function the ability to access and modify the
caller’s argument data directly
Performance Tip
• One disadvantage of pass-by-value is that, if a large data item is being passed, copying that data can take a considerable amount of execution time and memory space.
References and Reference Parameters (Cont.)
• Reference Parameter– When you need to manipulate from inside a function the
value of an external variable
– An alias for its corresponding argument in a function call
– & placed after the parameter type in the function prototype and function header
– Example• int &count in a function header
– Pronounced as “count is a reference to an int”
– Parameter name in the body of the called function actually refers to the original variable in the calling function
Performance Tip
• Pass-by-reference is good for performance reasons, because it can eliminate the pass-by-value overhead of copying large amounts of data.
Software Engineering Observation
• Pass-by-reference can weaken security, because the called function can corrupt the caller’s data.
1 // Fig. 6.19: fig06_19.cpp
2 // Comparing pass-by-value and pass-by-reference with references.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int squareByValue( int ); // function prototype (value pass)
8 void squareByReference( int & ); // function prototype (reference pass)
9
10 int main()
11 {
12 int x = 2; // value to square using squareByValue
13 int z = 4; // value to square using squareByReference
14
15 // demonstrate squareByValue
16 cout << "x = " << x << " before squareByValue\n";
17 cout << "Value returned by squareByValue: "
18 << squareByValue( x ) << endl;
19 cout << "x = " << x << " after squareByValue\n" << endl;
20
21 // demonstrate squareByReference
22 cout << "z = " << z << " before squareByReference" << endl;
23 squareByReference( z );
24 cout << "z = " << z << " after squareByReference" << endl;
25 return 0; // indicates successful termination
26 } // end main
27
Outline
•fig06_19.cpp
•(1 of 2)
Function illustrating pass-by-value
Function illustrating pass-by-reference
Variable is simply mentioned by name in both function calls
28 // squareByValue multiplies number by itself, stores the
29 // result in number and returns the new value of number
30 int squareByValue( int number )
31 {
32 return number *= number; // caller's argument not modified
33 } // end function squareByValue
34
35 // squareByReference multiplies numberRef by itself and stores the result
36 // in the variable to which numberRef refers in function main
37 void squareByReference( int &numberRef )
38 {
39 numberRef *= numberRef; // caller's argument modified
40 } // end function squareByReference x = 2 before squareByValue Value returned by squareByValue: 4 x = 2 after squareByValue z = 4 before squareByReference z = 16 after squareByReference
Outline
•fig06_19.cpp
•(2 of 2)
Receives copy of argument in main
Receives reference to argument in main
Modifies variable in main
Common Programming Errors
• Because reference parameters are mentioned only by name in the body of the called function, the programmer might inadvertently treat reference parameters as pass-by-value parameters. This can cause unexpected side effects if the original copies of the variables are changed by the function.
Performance Tip
• For passing large objects, use a constant reference parameter to simulate the appearance and security of pass-by-value and avoid the overhead of passing a copy of the large object.
Software Engineering Observation
• For the combined reasons of clarity and performance, many C++ programmers prefer that modifiable arguments be passed to functions by using pointers (which we study in Chapter 8), small nonmodifiable arguments be passed by value and large nonmodifiable arguments be passed to functions by using references to constants.
References and Reference Parameters (Cont.)
• References– Can also be used as aliases for other variables within a
function• All operations supposedly performed on the alias (i.e., the
reference) are actually performed on the original variable
• An alias is simply another name for the original variable
• Must be initialized in their declarations
– Cannot be reassigned afterward
– Example• int count = 1;int &cRef = count;cRef++;
– Increments count through alias cRef
1 // Fig. 6.20: fig06_20.cpp
2 // References must be initialized.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 int x = 3;
10 int &y = x; // y refers to (is an alias for) x
11
12 cout << "x = " << x << endl << "y = " << y << endl;
13 y = 7; // actually modifies x
14 cout << "x = " << x << endl << "y = " << y << endl;
15 return 0; // indicates successful termination
16 } // end main x = 3 y = 3 x = 7 y = 7
Outline
•fig06_20.cpp
•(1 of 1)
Creating a reference as an alias to another variable in the function
Assign 7 to x through alias y
1 // Fig. 6.21: fig06_21.cpp
2 // References must be initialized.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 int x = 3;
10 int &y; // Error: y must be initialized
11
12 cout << "x = " << x << endl << "y = " << y << endl;
13 y = 7;
14 cout << "x = " << x << endl << "y = " << y << endl;
15 return 0; // indicates successful termination
16 } // end main
Borland C++ command-line compiler error message: Error E2304 C:\cpphtp5_examples\ch06\Fig06_21\fig06_21.cpp 10: Reference variable 'y' must be initialized in function main()
Microsoft Visual C++ compiler error message: C:\cpphtp5_examples\ch06\Fig06_21\fig06_21.cpp(10) : error C2530: 'y' : references must be initialized
GNU C++ compiler error message: fig06_21.cpp:10: error: 'y' declared as a reference but not initialized
Outline
•fig06_21.cpp
•(1 of 2)
Uninitialized reference
References and Reference Parameters (Cont.)
• Returning a reference from a function– Functions can return references to variables
• Should only be used when the variable is static
– Dangling reference• Returning a reference to an automatic variable
– That variable no longer exists after the function ends
Common Programming Error
• Not initializing a reference variable when it is declared is a compilation error, unless the declaration is part of a function’s parameter list. Reference parameters are initialized when the function in which they are declared is called.
Common Programming Error
• Attempting to reassign a previously declared reference to be an alias to another variable is a logic error. The value of the other variable is simply assigned to the variable for which the reference is already an alias.
Common Programming Error
• Returning a reference to an automatic variable in a called function is a logic error. Some compilers issue a warning when this occurs.
Default Arguments
• Default argument– A default value to be passed to a parameter
• Used when the function call does not specify an argument for that parameter
– Must be the rightmost argument(s) in a function’s parameter list
– Should be specified with the first occurrence of the function name
• Typically the function prototype
Common Programming Error
• It is a compilation error to specify default arguments in both a function’s prototype and header.
1 // Fig. 6.22: fig06_22.cpp
2 // Using default arguments.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 // function prototype that specifies default arguments
8 int boxVolume( int length = 1, int width = 1, int height = 1 );
9
10 int main()
11 {
12 // no arguments--use default values for all dimensions
13 cout << "The default box volume is: " << boxVolume();
14
15 // specify length; default width and height
16 cout << "\n\nThe volume of a box with length 10,\n"
17 << "width 1 and height 1 is: " << boxVolume( 10 );
18
19 // specify length and width; default height
20 cout << "\n\nThe volume of a box with length 10,\n"
21 << "width 5 and height 1 is: " << boxVolume( 10, 5 );
22
23 // specify all arguments
24 cout << "\n\nThe volume of a box with length 10,\n"
25 << "width 5 and height 2 is: " << boxVolume( 10, 5, 2 )
26 << endl;
27 return 0; // indicates successful termination
28 } // end main
Outline
•fig06_22.cpp
•(1 of 2)Default arguments
Calling function with no arguments
Calling function with one argument
Calling function with two arguments
Calling function with three arguments
29
30 // function boxVolume calculates the volume of a box
31 int boxVolume( int length, int width, int height )
32 {
33 return length * width * height;
34 } // end function boxVolume The default box volume is: 1 The volume of a box with length 10, width 1 and height 1 is: 10 The volume of a box with length 10, width 5 and height 1 is: 50 The volume of a box with length 10, width 5 and height 2 is: 100
Outline
•fig06_22.cpp
•(2 of 2)
Note that default arguments were specified in the function prototype, so they are not specified in the function header
Good Programming Practice
• Using default arguments can simplify writing function calls. However, some programmers feel that explicitly specifying all arguments is clearer.
Unary Scope Resolution Operator
• Unary scope resolution operator (::)– Used to access a global variable when a local variable of
the same name is in scope
– Cannot be used to access a local variable of the same name in an outer block
Good Programming Practice
• Always using the unary scope resolution operator (::) to refer to global variables makes programs easier to read and understand, because it makes it clear that you are intending to access a global variable rather than a nonglobal variable.
1 // Fig. 6.23: fig06_23.cpp
2 // Using the unary scope resolution operator.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int number = 7; // global variable named number
8
9 int main()
10 {
11 double number = 10.5; // local variable named number
12
13 // display values of local and global variables
14 cout << "Local double value of number = " << number
15 << "\nGlobal int value of number = " << ::number << endl;
16 return 0; // indicates successful termination
17 } // end main Local double value of number = 10.5 Global int value of number = 7
Outline
•fig06_23.cpp
•(1 of 1)
Unary scope resolution operator used to access global variable number
Software Engineering Observation
• Always using the unary scope resolution operator (::) to refer to global variables makes programs easier to modify by reducing the risk of name collisions with nonglobal variables.
Error-Prevention Tip
• Always using the unary scope resolution operator (::) to refer to a global variable eliminates possible logic errors that might occur if a nonglobal variable hides the global variable.
Error-Prevention Tip
• Avoid using variables of the same name for different purposes in a program. Although this is allowed in various circumstances, it can lead to errors.
Function Overloading
• Overloaded functions– Overloaded functions have
• Same name
• Different sets of parameters
– Compiler selects proper function to execute based on number, types and order of arguments in the function call
– Commonly used to create several functions of the same name that perform similar tasks, but on different data types
Good Programming Practice
• Overloading functions that perform closely related tasks can make programs more readable and understandable.
1 // Fig. 6.24: fig06_24.cpp
2 // Overloaded functions.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 // function square for int values
8 int square( int x )
9 {
10 cout << "square of integer " << x << " is ";
11 return x * x;
12 } // end function square with int argument
13
14 // function square for double values
15 double square( double y )
16 {
17 cout << "square of double " << y << " is ";
18 return y * y;
19 } // end function square with double argument
Outline
•fig06_24.cpp
•(1 of 2)
Defining a square function for ints
Defining a square function for doubles
20
21 int main()
22 {
23 cout << square( 7 ); // calls int version
24 cout << endl;
25 cout << square( 7.5 ); // calls double version
26 cout << endl;
27 return 0; // indicates successful termination
28 } // end main square of integer 7 is 49 square of double 7.5 is 56.25
Outline
•fig06_24.cpp
•(2 of 2)
Output confirms that the proper function was called in each case
Function Overloading (Cont.)
• How the compiler differentiates overloaded functions – Overloaded functions are distinguished by their signatures
– Name mangling or name decoration • Compiler encodes each function identifier with the number
and types of its parameters to enable type-safe linkage
– Type-safe linkage ensures that• Proper overloaded function is called
• Types of the arguments conform to types of the parameters
1 // Fig. 6.25: fig06_25.cpp
2 // Name mangling.
3
4 // function square for int values
5 int square( int x )
6 {
7 return x * x;
8 } // end function square
9
10 // function square for double values
11 double square( double y )
12 {
13 return y * y;
14 } // end function square
15
16 // function that receives arguments of types
17 // int, float, char and int &
18 void nothing1( int a, float b, char c, int &d )
19 {
20 // empty function body
21 } // end function nothing1
Outline
•fig06_25.cpp
•(1 of 2)Overloaded square functions
22
23 // function that receives arguments of types
24 // char, int, float & and double &
25 int nothing2( char a, int b, float &c, double &d )
26 {
27 return 0;
28 } // end function nothing2
29
30 int main()
31 {
32 return 0; // indicates successful termination
33 } // end main @square$qi @square$qd @nothing1$qifcri @nothing2$qcirfrd _main
Outline
•fig06_25.cpp
•(2 of 2)
main is not mangled because it cannot be overloaded
Mangled names of overloaded functions
Common Programming Error
• Creating overloaded functions with identical parameter lists and different return types is a compilation error.
Common Programming Error
• A function with default arguments omitted might be called identically to another overloaded function; this is a compilation error. For example, having in a program both a function that explicitly takes no arguments and a function of the same name that contains all default arguments results in a compilation error when an attempt is made to use that function name in a call passing no arguments. The compiler does not know which version of the function to choose.
Passing Arrays to Functions
• To pass an array argument to a function– Specify array name without brackets
• Array hourlyTemperatures is declared as
int hourlyTemperatures[ 24 ];• The function call
modifyArray( hourlyTemperatures, 24 );
passes array hourlyTemperatures and its size to function modifyArray
– Array size is normally passed as another argument so the function can process the specific number of elements in the array
Passing Arrays to Functions (Cont.)
• Arrays are passed by reference– Function call actually passes starting address of array
• So function knows where array is located in memory
– Caller gives called function direct access to caller’s data• Called function can manipulate this data
Performance Tip
• Passing arrays by reference makes sense for performance reasons. If arrays were passed by value, a copy of each element would be passed. For large, frequently passed arrays, this would be time consuming and would require considerable storage for the copies of the array elements.
Passing Arrays to Functions (Cont.)
• Individual array elements passed by value– Single pieces of data
• Known as scalars or scalar quantities
– To pass an element to a function• Use the subscripted name of the array element as an
argument
• Functions that take arrays as arguments– Function parameter list must specify array parameter
• Example
– void modArray( int b[], int arraySize );
Passing Arrays to Functions (Cont.)
• Functions that take arrays as arguments (Cont.)– Array parameter may include the size of the array
• Compiler will ignore it, though
– Compiler only cares about the address of the first element
• Function prototypes may include parameter names– But the compiler will ignore them
– Parameter names may be left out of function prototypes
1 // Fig. 7.14: fig07_14.cpp
2 // Passing arrays and individual array elements to functions.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include <iomanip>
8 using std::setw;
9
10 void modifyArray( int [], int ); // appears strange
11 void modifyElement( int );
12
13 int main()
14 {
15 const int arraySize = 5; // size of array a
16 int a[ arraySize ] = { 0, 1, 2, 3, 4 }; // initialize array a
17
18 cout << "Effects of passing entire array by reference:"
19 << "\n\nThe values of the original array are:\n";
20
21 // output original array elements
22 for ( int i = 0; i < arraySize; i++ )
23 cout << setw( 3 ) << a[ i ];
24
25 cout << endl;
26
27 // pass array a to modifyArray by reference
28 modifyArray( a, arraySize );
29 cout << "The values of the modified array are:\n";
Outline
•fig07_14.cpp
•(1 of 3)Declare 5-int array array with initializer list
Function takes an array as argument
Pass entire array to function modifyArray
30
31 // output modified array elements
32 for ( int j = 0; j < arraySize; j++ )
33 cout << setw( 3 ) << a[ j ];
34
35 cout << "\n\n\nEffects of passing array element by value:"
36 << "\n\na[3] before modifyElement: " << a[ 3 ] << endl;
37
38 modifyElement( a[ 3 ] ); // pass array element a[ 3 ] by value
39 cout << "a[3] after modifyElement: " << a[ 3 ] << endl;
40
41 return 0; // indicates successful termination
42 } // end main
43
44 // in function modifyArray, "b" points to the original array "a" in memory
45 void modifyArray( int b[], int sizeOfArray )
46 {
47 // multiply each array element by 2
48 for ( int k = 0; k < sizeOfArray; k++ )
49 b[ k ] *= 2;
50 } // end function modifyArray
Outline
•fig07_14.cpp
•(2 of 3)
Pass array element a[ 3 ] to function modifyElement
Function modifyArray manipulates the array directly
51
52 // in function modifyElement, "e" is a local copy of
53 // array element a[ 3 ] passed from main
54 void modifyElement( int e )
55 {
56 // multiply parameter by 2
57 cout << "Value of element in modifyElement: " << ( e *= 2 ) << endl;
58 } // end function modifyElement Effects of passing entire array by reference: The values of the original array are: 0 1 2 3 4 The values of the modified array are: 0 2 4 6 8 Effects of passing array element by value: a[3] before modifyElement: 6 Value of element in modifyElement: 12 a[3] after modifyElement: 6
Outline
•fig07_14.cpp
•(3 of 3)
Function modifyElement manipulates array element’s copy
Passing Arrays to Functions (Cont.)
•const array parameters– Qualifier const
– Prevent modification of array values in the caller by code in the called function
– Elements in the array are constant in the function body
– Enables programmer to prevent accidental modification of data
1 // Fig. 7.15: fig07_15.cpp
2 // Demonstrating the const type qualifier.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 void tryToModifyArray( const int [] ); // function prototype
8
9 int main()
10 {
11 int a[] = { 10, 20, 30 };
12
13 tryToModifyArray( a );
14 cout << a[ 0 ] << ' ' << a[ 1 ] << ' ' << a[ 2 ] << '\n';
15
16 return 0; // indicates successful termination
17 } // end main
18
Outline
•fig07_15.cpp
•(1 of 2)
Using const to prevent the function from modifying the array
Array a will be const when in the body of the function
19 // In function tryToModifyArray, "b" cannot be used
20 // to modify the original array "a" in main.
21 void tryToModifyArray( const int b[] )
22 {
23 b[ 0 ] /= 2; // error
24 b[ 1 ] /= 2; // error
25 b[ 2 ] /= 2; // error
26 } // end function tryToModifyArray
Borland C++ command-line compiler error message: Error E2024 fig07_15.cpp 23: Cannot modify a const object in function tryToModifyArray(const int * const) Error E2024 fig07_15.cpp 24: Cannot modify a const object in function tryToModifyArray(const int * const) Error E2024 fig07_15.cpp 25: Cannot modify a const object in function tryToModifyArray(const int * const)
Microsoft Visual C++.NET compiler error message: C:\cpphtp5_examples\ch07\fig07_15.cpp(23) : error C2166: l-value specifies const object C:\cpphtp5_examples\ch07\fig07_15.cpp(24) : error C2166: l-value specifies const object C:\cpphtp5_examples\ch07\fig07_15.cpp(25) : error C2166: l-value specifies const object
GNU C++ compiler error message: fig07_15.cpp:23: error: assignment of read-only location fig07_15.cpp:24: error: assignment of read-only location fig07_15.cpp:25: error: assignment of read-only location
Outline
•fig07_15.cpp
•(2 of 2)
Array cannot be modified; it is const within the body function
Common Programming Error
• Forgetting that arrays in the caller are passed by reference, and hence can be modified in called functions, may result in logic errors.
Software Engineering Observation
• Applying the const type qualifier to an array parameter in a function definition to prevent the original array from being modified in the function body is another example of the principle of least privilege. Functions should not be given the capability to modify an array unless it is absolutely necessary.
Searching Arrays with Linear Search
• Arrays may store large amounts of data– May need to determine if certain key value is located in an array
• Linear search– Compares each element of an array with a search key
– Just as likely that the value will be found in the first element as the last
• On average, program must compare the search key with half the elements of the array
– To determine that value is not in array, program must compare the search key to every element in the array
– Works well for small or unsorted arrays
1 // Fig. 7.19: fig07_19.cpp
2 // Linear search of an array.
3 #include <iostream>
4 using std::cout;
5 using std::cin;
6 using std::endl;
7
8 int linearSearch( const int [], int, int ); // prototype
9
10 int main()
11 {
12 const int arraySize = 100; // size of array a
13 int a[ arraySize ]; // create array a
14 int searchKey; // value to locate in array a
15
16 for ( int i = 0; i < arraySize; i++ )
17 a[ i ] = 2 * i; // create some data
18
19 cout << "Enter integer search key: ";
20 cin >> searchKey;
21
22 // attempt to locate searchKey in array a
23 int element = linearSearch( a, searchKey, arraySize );
24
Outline
•fig07_19.cpp
•(1 of 2)
Function takes an array, a key value, and the size of the array as arguments
Function returns location of key value, -1 if not found
Outline
•fig07_19.cpp
•(2 of 2)
25 // display results
26 if ( element != -1 )
27 cout << "Found value in element " << element << endl;
28 else
29 cout << "Value not found" << endl;
30
31 return 0; // indicates successful termination
32 } // end main
33
34 // compare key to every element of array until location is
35 // found or until end of array is reached; return subscript of
36 // element if key or -1 if key not found
37 int linearSearch( const int array[], int key, int sizeOfArray )
38 {
39 for ( int j = 0; j < sizeOfArray; j++ )
40 if ( array[ j ] == key ) // if found,
41 return j; // return location of key
42
43 return -1; // key not found
44 } // end function linearSearch Enter integer search key: 36 Found value in element 18 Enter integer search key: 37 Value not found
Return location if current value equals key value
Search through entire array
Recursion
• Recursive function– A function that calls itself, either directly, or indirectly
(through another function)
• Recursion– Base case(s)
• The simplest case(s), which the function knows how to handle
– For all other cases, the function typically divides the problem into two conceptual pieces
• A piece that the function knows how to do
• A piece that it does not know how to do– Slightly simpler or smaller version of the original problem
Recursion (Cont.)
• Recursion (Cont.)– Recursive call (also called the recursion step)
• The function launches (calls) a fresh copy of itself to work on the smaller problem
• Can result in many more recursive calls, as the function keeps dividing each new problem into two conceptual pieces
• This sequence of smaller and smaller problems must eventually converge on the base case
– Otherwise the recursion will continue forever
Recursion (Cont.)
• Factorial– The factorial of a nonnegative integer n, written n! (and
pronounced “n factorial”), is the product• n · (n – 1) · (n – 2) · … · 1
– Recursive definition of the factorial function• n! = n · (n – 1)!
• Example
– 5! = 5 · 4 · 3 · 2 · 15! = 5 · ( 4 · 3 · 2 · 1)5! = 5 · ( 4! )
Fig. 6.28 | Recursive evaluation of 5!.
1 // Fig. 6.29: fig06_29.cpp
2 // Testing the recursive factorial function.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include <iomanip>
8 using std::setw;
9
10 unsigned long factorial( unsigned long ); // function prototype
11
12 int main()
13 {
14 // calculate the factorials of 0 through 10
15 for ( int counter = 0; counter <= 10; counter++ )
16 cout << setw( 2 ) << counter << "! = " << factorial( counter )
17 << endl;
18
19 return 0; // indicates successful termination
20 } // end main
Outline
fig06_29.cpp
(1 of 2)
First call to factorial function
21
22 // recursive definition of function factorial
23 unsigned long factorial( unsigned long number )
24 {
25 if ( number <= 1 ) // test for base case
26 return 1; // base cases: 0! = 1 and 1! = 1
27 else // recursion step
28 return number * factorial( number - 1 );
29 } // end function factorial 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800
Outline
fig06_29.cpp
(2 of 2)
Base cases simply return 1
Recursive call to factorial function with a slightly smaller problem
Common Programming Error 6.24
Either omitting the base case, or writing the recursion step incorrectly so that it does not converge on the base case, causes “infinite” recursion, eventually exhausting memory. This is analogous to the problem of an infinite loop in an iterative (nonrecursive) solution.
Example Using Recursion: Fibonacci Series
• The Fibonacci series– 0, 1, 1, 2, 3, 5, 8, 13, 21, …
– Begins with 0 and 1
– Each subsequent Fibonacci number is the sum of the previous two Fibonacci numbers
– can be defined recursively as follows:• fibonacci(0) = 0
• fibonacci(1) = 1
• fibonacci(n) = fibonacci(n – 1) + fibonacci(n – 2)
1 // Fig. 6.30: fig06_30.cpp
2 // Testing the recursive fibonacci function.
3 #include <iostream>
4 using std::cout;
5 using std::cin;
6 using std::endl;
7
8 unsigned long fibonacci( unsigned long ); // function prototype
9
10 int main()
11 {
12 // calculate the fibonacci values of 0 through 10
13 for ( int counter = 0; counter <= 10; counter++ )
14 cout << "fibonacci( " << counter << " ) = "
15 << fibonacci( counter ) << endl;
16
17 // display higher fibonacci values
18 cout << "fibonacci( 20 ) = " << fibonacci( 20 ) << endl;
19 cout << "fibonacci( 30 ) = " << fibonacci( 30 ) << endl;
20 cout << "fibonacci( 35 ) = " << fibonacci( 35 ) << endl;
21 return 0; // indicates successful termination
22 } // end main
23
Outline
fig06_30.cpp
(1 of 2)
24 // recursive method fibonacci
25 unsigned long fibonacci( unsigned long number )
26 {
27 if ( ( number == 0 ) || ( number == 1 ) ) // base cases
28 return number;
29 else // recursion step
30 return fibonacci( number - 1 ) + fibonacci( number - 2 );
31 } // end function fibonacci fibonacci( 0 ) = 0 fibonacci( 1 ) = 1 fibonacci( 2 ) = 1 fibonacci( 3 ) = 2 fibonacci( 4 ) = 3 fibonacci( 5 ) = 5 fibonacci( 6 ) = 8 fibonacci( 7 ) = 13 fibonacci( 8 ) = 21 fibonacci( 9 ) = 34 fibonacci( 10 ) = 55 fibonacci( 20 ) = 6765 fibonacci( 30 ) = 832040 fibonacci( 35 ) = 9227465
Outline
fig06_30.cpp
(2 of 2)
Recursive calls to fibonacci function
Base cases
Fig. 6.31 | Set of recursive calls to function fibonacci.
Common Programming Error
Writing programs that depend on the order of evaluation of the operands of operators other than &&, ||, ?: and the comma (,) operator can lead to logic errors.
Portability Tip
Programs that depend on the order of evaluation of the operands of operators other than &&, ||, ?: and the comma (,) operator can function differently on systems with different compilers.
Example Using Recursion: Fibonacci Series (Cont.)
• Caution about recursive programs– Each level of recursion in function fibonacci has a
doubling effect on the number of function calls• i.e., the number of recursive calls that are required to
calculate the nth Fibonacci number is on the order of 2n
• 20th Fibonacci number would require on the order of 220 or about a million calls
• 30th Fibonacci number would require on the order of 230 or about a billion calls.
– Exponential complexity• Can humble even the world’s most powerful computers
Performance Tip
Avoid Fibonacci-style recursive programs that result in an exponential “explosion” of calls.
Recursion vs. Iteration
• Both are based on a control statement– Iteration – repetition structure
– Recursion – selection structure
• Both involve repetition– Iteration – explicitly uses repetition structure
– Recursion – repeated function calls
• Both involve a termination test– Iteration – loop-termination test
– Recursion – base case
Recursion vs. Iteration (Cont.)
• Both gradually approach termination– Iteration modifies counter until loop-termination test fails
– Recursion produces progressively simpler versions of problem
• Both can occur infinitely– Iteration – if loop-continuation condition never fails
– Recursion – if recursion step does not simplify the problem
1 // Fig. 6.32: fig06_32.cpp
2 // Testing the iterative factorial function.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 #include <iomanip>
8 using std::setw;
9
10 unsigned long factorial( unsigned long ); // function prototype
11
12 int main()
13 {
14 // calculate the factorials of 0 through 10
15 for ( int counter = 0; counter <= 10; counter++ )
16 cout << setw( 2 ) << counter << "! = " << factorial( counter )
17 << endl;
18
19 return 0;
20 } // end main
21
22 // iterative function factorial
23 unsigned long factorial( unsigned long number )
24 {
25 unsigned long result = 1;
Outline
fig06_32.cpp
(1 of 2)
26
27 // iterative declaration of function factorial
28 for ( unsigned long i = number; i >= 1; i-- )
29 result *= i;
30
31 return result;
32 } // end function factorial 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800
Outline
fig06_32.cpp
(2 of 2)
Iterative approach to finding a factorial
Recursion vs. Iteration (Cont.)
• Negatives of recursion– Overhead of repeated function calls
• Can be expensive in both processor time and memory space
– Each recursive call causes another copy of the function (actually only the function’s variables) to be created
• Can consume considerable memory
• Iteration – Normally occurs within a function
– Overhead of repeated function calls and extra memory assignment is omitted
Software Engineering Observation
Any problem that can be solved recursively can also be solved iteratively (nonrecursively). A recursive approach is normally chosen in preference to an iterative approach when the recursive approach more naturally mirrors the problem and results in a program that is easier to understand and debug. Another reason to choose a recursive solution is that an iterative solution is not apparent.
Performance Tip
Avoid using recursion in performance situations. Recursive calls take time and consume additional memory.
Common Programming Error
Accidentally having a nonrecursive function call itself, either directly or indirectly (through another function), is a logic error.