Section 7.3. The return Statement


7.3. The return Statement

A return statement terminates the function that is currently executing and returns control to the function that called the now-terminated function. There are two forms of return statements:

      return;      return expression; 

7.3.1. Functions with No Return Value

A return with no value may be used only in a function that has a return type of void. Functions that return void are not required to contain a return statement. In a void function, an implicit return takes place after the function's final statement.

Typically, a void function uses a return to cause premature termination of the function. This use of return parallels the use of the break (Section 6.10, p. 212) statement inside a loop. For example, we could rewrite our swap program to avoid doing any work if the values are identical:

      // ok: swap acts on references to its arguments      void swap(int &v1, int &v2)      {           // if values already the same, no need to swap, just return           if (v1 == v2)               return;           // ok, have work to do           int tmp = v2;           v2 = v1;           v1 = tmp;           // no explicit return necessary      } 

This function first checks if the values are equal and if so exits the function. If the values are unequal, the function swaps them. An implicit return occurs after the last assignment statement.

A function with a void return type ordinarily may not use the second form of the return statement. However, a void function may return the result of calling another function that returns void:

      void do_swap(int &v1, int &v2)      {          int tmp = v2;          v2 = v1;          v1 = tmp;          // ok: void function doesn't need an explicit return      }      void swap(int &v1, int &v2)      {          if (v1 == v2)              return false; // error: void function cannot return a value          return do_swap(v1, v2); // ok: returns call to a void function      } 

Attempting to return any other expression is a compile-time error.

7.3.2. Functions that Return a Value

The second form of the return statement provides the function's result. Every return in a function with a return type other than void must return a value. The value returned must have the same type as the function return type, or must have a type that can be implicitly converted to that type.

Although C++ cannot guarantee the correctness of a result, it can guarantee that every return from a function returns a result of the appropriate type. The following program, for example, won't compile:

      // Determine whether two strings are equal.      // If they differ in size, determine whether the smaller      // one holds the same characters as the larger one      bool str_subrange(const string &str1, const string &str2)      {          // same sizes: return normal equality test          if (str1.size() == str2.size())              return str1 == str2;    // ok, == returns bool          // find size of smaller string          string::size_type size = (str1.size() < str2.size())                                   ? str1.size() : str2.size();          string::size_type i = 0;          // look at each element up to size of smaller string          while (i != size) {              if (str1[i] != str2[i])                  return;   // error: no return value          }          // error: control might flow off the end of the function without a return          // the compiler is unlikely to detect this error       } 

The return from within the while loop is an error because it fails to return a value. The compiler should detect this error.

The second error occurs because the function fails to provide a return after the while loop. If we call this function with one string that is a subset of the other, execution would fall out of the while. There should be are turn to handle this case. The compiler may or may not detect this error. If a program is generated, what happens at run time is undefined.

Failing to provide a return after a loop that does contain a return is particularly insidious because many compilers will not detect it. The behavior at run time is undefined.



Return from main

There is one exception to the rule that a function with a return type other than void must return a value: The main function is allowed to terminate without a return. If control reaches the end of main and there is no return, then the compiler implicitly inserts a return of 0.

Another way in which the return from main is special is how its returned value is treated. As we saw in Section 1.1 (p. 2), the value returned from main is treated as a status indicator. A zero return indicates success; most other values indicate failure. A nonzero value has a machine-dependent meaning. To make return values machine-independent, the cstdlib header defines two preprocessor variables (Section 2.9.2, p. 69) that we can use to indicate success or failure:

      #include <cstdlib>      int main()      {          if (some_failure)              return EXIT_FAILURE;          else              return EXIT_SUCCESS;      } 

Our code no longer needs to use the precise machine-dependent values. Instead, those values are defined in cstdlib, and our code need not change.

Returning a Nonreference Type

The value returned by a function is used to initialize a temporary object created at the point at which the call was made. A temporary object is an unnamed object created by the compiler when it needs a place to store a result from evaluating an expression. C++ programmers usually use the term "temporary" as an abreviation of "temporary object."

The temporary is initialized by the value returned by a function in much the same way that parameters are initialized by their arguments. If the return type is not a reference, then the return value is copied into the temporary at the call site. The value returned when a function returns a nonreference type can be a local object or the result of evaluating an expression.

As an example, we might want to write a function that, given a counter, a word, and an ending, gives us back the plural version of the word if the counter is greater than one:

      // return plural version of word if ctr isn't 1      string make_plural(size_t ctr, const string &word,                                     const string &ending)      {          return (ctr == 1) ? word : word + ending;      } 

We might use such a function to print a message with either a plural or singular ending.

This function either returns a copy of its parameter named word or it returns an unnamed temporary string that results from adding word and ending. In either case, the return copies that string to the call site.

Returning a Reference

When a function returns a reference type, the return value is not copied. Instead, the object itself is returned. As an example, consider a function that returns a reference to the shorter of its two string parameters:

      // find longer of two strings      const string &shorterString(const string &s1, const string &s2)      {          return s1.size() < s2.size() ? s1 : s2;      } 

The parameters and return type are references to const string. The strings are not copied either when calling the function or when returning the result.

Never Return a Reference to a Local Object

There's one crucially important thing to understand about returning a reference: Never return a reference to a local variable.



When a function completes, the storage in which the local objects were allocated is freed. A reference to a local object refers to undefined memory after the function terminates. Consider the following function:

      // Disaster: Function returns a reference to a local object      const string &manip(const string& s)      {           string ret = s;           // transform ret in some way           return ret; // Wrong: Returning reference to a local object!      } 

This function will fail at run time because it returns a reference to a local object. When the function ends, the storage in which ret resides is freed. The return value refers to memory that is no longer available to the program.

One good way to ensure that the return is safe is to ask: To what pre-existing object is the reference referring?



Reference Returns Are Lvalues

A function that returns a reference returns an lvalue. That function, therefore, can be used wherever an lvalue is required:

      char &get_val(string &str, string::size_type ix)      {          return str[ix];      }      int main()      {          string s("a value");          cout << s << endl;   // prints a value          get_val(s, 0) = 'A'; // changes s[0] to A          cout << s << endl;   // prints A value          return 0;      } 

It may be surprising to assign to the return of a function, but the return is a reference. As such, it is just a synonym for the element returned.

If we do not want the reference return to be modifiable, the return value should be declared as const:

      const char &get_val(... 

Never Return a Pointer to a Local Object

The return type for a function can be most any type. In particular, it is possible for a function to return a pointer. For the same reasons that it is an error to return a reference to a local object, it is also an error to return a pointer to a local object. Once the function completes, the local objects are freed. The pointer would be a dangling pointer (Section 5.11, p. 176) that refers to a nonexistent object.

7.3.3. Recursion

A function that calls itself, either directly or indirectly, is a recursive function. An example of a simple recursive function is one that computes the factorial of a number. The factorial of a number n is the product of the numbers from 1 to n. The factorial of 5, for example, is 120.

      1 * 2 * 3 * 4 * 5 = 120 

Exercises Section 7.3.2

Exercise 7.17:

When is it valid to return a reference? A const reference?

Exercise 7.18:

What potential run-time problem does the following function have?

      string &processText() {          string text;          while (cin >> text) { /* ... */ }          // ....          return text;      } 

Exercise 7.19:

Indicate whether the following program is legal. If so, explain what it does; if not, make it legal and then explain it:

      int &get(int *arry, int index) { return arry[index]; }      int main() {          int ia[10];          for (int i = 0; i != 10; ++i)               get(ia, i) = 0;      } 

A natural way to solve this problem is recursively:

      // calculate val!, which is 1*2 *3 ... * val      int factorial(int val)      {          if (val > 1)              return factorial(val-1) * val;           return 1;      } 


A recursive function must always define a stopping condition; otherwise, the function will recurse "forever," meaning that the function will continue to call itself until the program stack is exhausted. This is sometimes called an "infinite recursion error." In the case of factorial, the stopping condition occurs when val is 1.

As another example, we can define a recursive function to find the greatest common divisor:

      // recursive version greatest common divisor program      int rgcd(int v1, int v2)      {          if (v2 != 0)                // we're done once v2 gets to zero              return rgcd(v2, v1%v2); // recurse, reducing v2 on each call          return v1;      } 

In this case the stopping condition is a remainder of 0. If we call rgcd with the arguments (15, 123), then the result is three. Table 7.1 on the next page traces the execution.

Table 7.1. Trace of rgcd(15,123)

v1

v2

Return

15

123

rgcd(123, 15)

123

15

rgcd(15, 3)

15

3

rgcd(3, 0)

3

0

3


The last call,

      rgcd(3,0) 

satisfies the stopping condition. It returns the greatest common denominator, 3. This value successively becomes the return value of each prior call. The value is said to percolate upward until the execution returns to the function that called rgcd in the first place.

The main function may not call itself.



Exercises Section 7.3.3

Exercise 7.20:

Rewrite factorial as an iterative function.

Exercise 7.21:

What would happen if the stopping condition in factorial were:

      if (val != 0) 




C++ Primer
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2006
Pages: 223
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net