Call by Value Call by Reference Review The contents of this particular lecture prepared by the instructors at the University of Manitoba in Canada
Void and Non-Void Functions - Every function has a type - Part of the header, tells C++ what type of value the function will give back when we call it - The first word of the header defines the type of the function - The type void is used to indicate that the function is not returning anything - If you want to return a value, simply supply the type you want instead of void
Non- Void Functions int mypower (int x, int y) { // raise x to power y (y >= 0) using a count- controlled loop int count= 1; // loop counter int answer= 1; // holds the answer while (count <= y) { // loop to print 1 char @ a time answer = answer * x; count++; } // end while return answer; } // send answer back to caller Note >1 parameter Causes function to terminate and send back value we’ve specified
Calling Non-Void Functions - When we call this function, just like most built-in functions, we need to do something with the result: - Store it: answer = mypower(x,y); - Print it: cout << mypower(2,3); - Pass it on to other calculations: x = 2 + mypower(7,14); y= sqrt( mypower(2,4)); - Remember that the order of arguments matters!!!
Prototypes for Non-Void Functions - For a prototype of this function, we need to add the type information (just as we needed to use void in earlier examples) int mypower( int x, int y); - Remember that you can actually put the parameter names if you want, but they’re irrelevant
More on Return - Return may also be useful in terminating a function prematurely - You can use it anywhere in the function, and you can use it without a value in a void function to simply cause the function to terminate early - Example: while (1) { cin >> x; if (x < 0) return; else // lots of other code }
More on Return - Is also commonly used in the main function - we usually give the main function a type of int, and return some integer indicating whether the program ran correctly - e. g. return 0 for no error, higher numbers for more severe problems int main () { // whatever main does return 0; } - The text uses this; feel free to use it if you want
Example int strange( int); int main() { int arg = 7; cout << strange(arg); cout << endl << arg ; } int strange( int y) y = 6; return 0; < See Example 1> We print the result of calling strange, which is 0 (the value returned by strange) But what’s happening here? We print arg, which is 7 Or is it? When we call strange, we change it’s parameter to 6….
Altering Parameter Values - According to what we know happens with parameters, 7 should be printed - That’s because - - the parameter y is created when the function runs - the argument is copied into it (the 7)... - and even though we change y to 6, y itself is destroyed when the function returns - So our original argument, arg (with the value 7) is never touched and can’t be!
Parameter Types - But it doesn’t always have to be this way! - We are making use of only one of two types of parameters allowed by C++ - These are called Value Parameters, because as the name implies, the argument values are copied into the parameter, and the original argument can never be changed - There’s a second type that DOES allow argument values to be changed
Reference Parameters - This second type is called a Reference Parameter - For the moment, just assume that when you declare one, you’re allowed to actually make changes to the argument being passed - We can and do use this to return values from functions - Since we can have as many parameters as we want, we can return as many values as we’d like in them by altering the parameters!
Reference Parameters - Very easy to declare - just put an ampersand (&) at the end of the data type of the parameter name: int& x - So, when writing functions, just ask yourself if you want the original argument to be changeable, and if you do, add the ampersand! - Let’s see our strange example with reference parameters...
Example int strange( int&); int main() { int arg = 7; cout << strange(arg); cout << endl << arg ; } int strange( int& y) y = 6; return 0; < See: Example 2> The parameter y is now a Reference Parameter - this means that whatever we pass to y can be altered!
Example int strange( int&); int main() { int arg = 7; cout << strange(arg); cout << endl << arg ; } int strange(int& y) y = 6; return 0; So arg is passed to strange y will be 7 as before y will be set to the value 6...
Example int strange( int&); int main() { int arg = 7; cout << strange( arg); cout << endl << arg ; } int strange( int& y) y = 6; int answer = y*2; return answer; - Obviously, we have to be careful with these! arg, after the call to strange, will also have the value 6!
- More importantly , considering the full example: int strange( int& ); int main() { int arg = 7; cout << strange( arg); cout << endl << arg ; } int strange( int& y) { y = 6; return 0; So when strange returns,arg has the value 6, and that is what is printed!
Returning Multiple Values - We can use reference parameters to return as many values as we want - See the next example
Returning Multiple Values int main() { float l, w, h; getvalues(l, w, h); cout << “volume is “ << l*w*h; } //------------------------------------------------ void getvalues(float& len, float& wid, float& height) cout << “enter an integer length: ”; cin >> len; cout << “enter an integer width: ”; cin >> wid; cout << “enter an integer height: ”; cin >> height;
Returning Multiple Values - getvalues doesn’t return anything... - instead, it has three reference parameters - when we input values, we change these, and change the variables the caller supplies! - Getvalues takes l, w, h as arguments, and because each of its parameters is a reference parameter, it actually modifies l, w, and h, effectively allowing the function to give back three values < See: Example 3>
Returning Multiple Values - But suppose I did this! int main() { int l, w, h; getvalues( 6, 2, 3); // other code after this } - It doesn’t make sense does it? How can the function change a 6, or a 2??? It can only change what’s stored in a variable!
One Dimensional Arrays: Structured data types The contents of this particular lecture prepared by the instructors at the University of Manitoba in Canada and modified by Dr. Ahmad Reza Hadaegh
One Dimensional Arrays - Key observation: - arrays can be indexed with variables int main() { char line[ 80]; int i; for (i= 0; i< 80; i= i+ 1) cin >> line[ i]; for( i= 0; i< 80; i= i+ 1) cout << line[ i]; for( i= 79; i>= 0; i= i- 1) } Loop runs i= 0, i= 1, i= 2, ..., i= 79
One Dimensional Arrays - Arrays can be declared of any data type - char, int, float, .… - General format of array declaration: DataType ArrayName [ConstIntExpression]; - DataType is the data type of the array elements - ArrayName is the name of the array - ConstIntExpression is a literal int or int constant
One Dimensional Arrays - Examples of Array Declaration - float cmplx[2]; // entries: cmplx[0], cmplx[1] - int ivec[2000]; // entries: ivec[0], ..., ivec[1999] - const int arraylen= 1000; - char biglin[arraylen]; // array biglin has entries biglin[0],..., biglin[999];
One Dimensional Arrays - Accessing Array Components - General form of array access is: ArrayName[IndexExpression] - ArrayName is the name of the array - IndexExpression is any integer expression - When evaluated, an array access is just like any other accessed variable - treat it like you would any value of the array type
One Dimensional Arrays - Array Entry Assignment ivec[0]= 17; // ivec entry 0 assigned 17 ivec[0]= ivec[0]* 3+ 2; // ivec entry 0 assigned 53 ivec[ivec[0]-2]= 5; // ivec entry 51 assigned 5 cout << cmplx[0]; // print out cmplx entry 0 cin >> biglin[5]; // read biglin entry 5; foo( ivec[0]); // call function foo with value 53
One Dimensional Arrays - Array Storage - array entries are stored one after another in memory - the index is just the offset from the beginning of the array ivec[0] Memory Layout of int ivec[2000] ivec[1] Each location holds a single integer ivec[2] ……. ivec[1999]
One Dimensional Arrays // E. G: find the largest element in array of integers int main() { int ValueArray[500]; ...; // somehow move data into ValueArray int maxval, i; maxval= ValueArray[0]; for (i= 1; i< 500; i++) if (ValueArray[ i]> maxval) maxval= ValueArray[i]; cout << "Maximum value in array is " << maxval; } start out assuming ValueArray[ 0] is largest Update maxval if a larger value found
One Dimensional Arrays - Out of Bounds Array Indices - What does the following code do? char line[80]; line[80]= 'X'; - Code accesses element 80, but the last defined element in the array is line[79] - result is undetermined - it may even modify other variables - C++ does not check this - your program may do weird things - this is invalid code!
One Dimensional Arrays Initializing arrays - Like variables of type char, float and int, arrays can be initialized when declared: int littlearray[5]={2,4,6,8, 10}; float wintertemp[3] = {-40, -45, -50}; - This is especially important for const arrays const int IntsMod5[ 5]={ 0,1,2,3,4}; const float FunConstants[ 2]={3.14, 2.71}; // arrays of constants cannot be modified
Array Operations - C++ does not provide "Aggregate" array operations - Operations which act on entire array - Example: int x[100], y[100], z[100]; // two arrays of 100 ints x= y; // this does not assign contents of y to x x= y+z; // this does not assign contents of y+z to x
Array Operations - To assign the contents of array y to x, you need a loop: int i; int x[100], y[100]; for (i= 0; i< 100; i++) x[i]= y[i]; // assign entry y[i] to value x[i]
Array Operations - Also cannot - Output arrays: int arr[3]={ 1,2,3}; cout << arr; - Return arrays: return arr; - Operations can be defined (by user functions) - Not defined by automatically for most types
Arrays as Parameters
Introduction - Arrays can be passed as parameters to functions - some important (and annoying!) differences with passing other types of parameters - Main difference: - Arrays are always passed by reference - don't need (and don't use) an & - this is the only way to return an array - Modify a parameter
Arrays as Parameters const int arrsize= 50; // Example: copy one array of chars to another const int arrsize= 50; void CopyArray(char[], char[]); int main() { char a[arrsize], b[arrsize]; int i; for (i= 0; i< arrsize; i++) b[ i]= 'Z'; CopyArray( a, b); // copy contents of b to a } Prototype: array parameters do not include size
Arrays as Parameters void CopyArray( char x[], char y[]) { int i; // Copy array y to x void CopyArray( char x[], char y[]) { int i; for (i= 0; i< arrsize; i++) x[i]= y[i]; } No & necessary: x, y are always reference parameters Don't include array size in function header either: Size is not passed!!! Modifications to x are propagated to actual parameter of caller Using the global constant arrsize
Arrays as Parameters - Often compute with arrays of different sizes - don't want to use a global constant for size - Solution: - Pass the size as an additional parameter // Copy array y to x void CopyArray( char x[], char y[], int sz) { int i; for (i= 0; i< sz; i++) x[ i]= y[ i]; } sz is the size of the arrays x, y
Arrays as Parameters - To call the new CopyArray CopyArray( a, b, arrsize); - arrsize can be local to main instead of global - The fact that 1) arrays are always passed by reference 2) size information is not passed is an annoying limitation of C++ arrays
Constant Parameters - Useful to note explicitly that a parameter is not modified by a function - tells any person reading the code that they can expect parameter to be constant - tells compiler that it can assume parameter to be constant - Value parameters do this in some sense - changes not propagated to actual parameters - For arrays we can't call by value...
Constant Parameters - Declare that a parameter is constant with the const keyword void CopyArray(char x[], const char y[], int sz) { int i; for (i= 0; i< sz; i++) x[ i]= y[ i]; } < See: Example 4>
Array Example: Comparing arrays bool CompareArray(const int x[], const int y[]) { int i; for (i= 0; i< sz; i++) if (x[ i] != y[ i]) // compare x[i], y[i] return false; // return false if difference found } return true; both array parameters are declared const:Look, but don't touch!
Passing the Size of an Array - Consider the sz parameter in our copying function: void CopyArray( char x[], const char y[], int sz) { . } - What happens if what is passed to sz is bigger than the actual size of the array?
Passing the Size of an Array - Big problems, as we already know - the loop runs off the end of the array - But what happens if the value of sz is smaller than the actual size of the array? - Nothing really - we just don’t use part of it...
Passing the Size of an Array - Why would you ever want to do this? - Suppose we want to read integers into an array until end-of-file, then call a copy function (like that used earlier but for integers) to copy to another array - We don’t know how big the file is, so how can we declare a big enough array to hold it all?
Using only Part of an Array - We can’t have arrays that grow on the fly (there are data types like this that we will see later) - We have no choice at the moment other than setting an arbitrary bound that will be bigger than we need - e. g. say that we will handle any set of values up to 500, and declare our arrays that size.
Using only Part of an Array - Now suppose we write some code to read in the data values... we don’t know how many items there will be in a given file, but we can count them as we read
Using only Part of an Array const int arrsize= 500; int list[arrsize]; // the array will hold up to 500 values int count = 0; // counter to keep track of the number // of slots filled up in the array ifstream myfile; int item; // item from the file myfile. open(“eg1. txt”); myfile >> item; while (myfile && count < arrsize) { // stop @ end of file or no room list[ count] = item; // stick the item in the array count++; } When loop quits count-1 contains last slot used in array
Using only Part of an Array So if we wanted to call our copy array function: void CopyArray( int x[], const int y[], int sz) { int i; for (i= 0; i< sz; i++) x[i]= y[i]; } Note the modification - we copy up to and including slot sz because it’s a real slot and not just the absolute size of the array (this also uses integer arrays now)
Using only Part of an Array - So if we wanted to call our copy array function: void CopyArray( int x[], const int y[], int sz) { int i; for (i= 0; i<sz; i++) x[ i]= y[ i]; } - We could pass count (250) to it’s size parameter, and the loop would run up to and including 249 (count-1), copying only the part we used
Using only Part of an Array - That is, if after reading the data in, we used the following statements: int list2[arrsize]; // copied array CopyArray( list2,list, count); - Only the first 249 items would be copied - the remainder of list2 would be whatever junk was there before - Similarly, we could print only the used portion, or sort only the used portion - we just have to remember where the used portion ends!
Using only Part of an Array - This is commonly done - The only downside is that there will usually be wasted space - we always declare the full array even if we use all of it only rarely - Also a problem where we don’t know how big to make the array - We will discuss how to solve these problems later