Team LiB
Previous Section Next Section

7.3. The return Statement

7.3. return 语句

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 语句有两种形式:

     return;
     return expression;

7.3.1. Functions with No Return Value

7.3.1. 没有返回值的函数

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.

不带返回值的 return 语句只能用于返回类型为 void 的函数。在返回类型为 void 的函数中,return 返回语句不是必需的,隐式的 return 发生在函数的最后一个语句完成时。

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:

一般情况下,返回类型是 void 的函数使用 return 语句是为了引起函数的强制结束,这种 return 的用法类似于循环结构中的 break 语句(第 6.10 节)的作用。例如,可如下重写 swap 程序,使之在输入的两个数值相同时不执行任何工作:

     // 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.

这个函数首先检查两个值是否相等,如果相等则退出函数;如果不相等,则交换这两个值,隐式的 return 发生在最后一个赋值语句后。

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 的函数通常不能使用第二种形式的 return 语句,但是,它可以返回另一个返回类型同样是 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

7.3.2. 具有返回值的函数

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.

return 语句的第二种形式提供了函数的结果。任何返回类型不是 void 的函数必须返回一个值,而且这个返回值的类型必须和函数的返回类型相同,或者能隐式转化为函数的返回类型。

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:

尽管 C++ 不能确保结果的正确性,但能保证函数每一次 return 都返回适当类型的结果。例如,下面的程序就不能通过编译:

     // 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.

while 循环中的 return 语句是错误的,因为它没有返回任何值,编译器将检查出这个错误。

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.

第二个错误源于函数没有在 while 循环后提供 return 语句。调用这个函数时,如果一个 string 是另一个 string 的子集,执行会退出 while 循环。这里应该有一个 return 语句来处理这种情况。编译器有可能检查出也有可能检查不出这种错误。执行程序时,不确定在运行阶段会出现什么问题。

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 语句的循环后没有提供 return 语句是很危险的,因为大部分的编译器不能检测出这个漏洞,运行时会出现什么问题是不确定的。



Return from main
主函数 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.

返回类型不是 void 的函数必须返回一个值,但此规则有一个例外情况:允许主函数 main 没有返回值就可结束。如果程序控制执行到主函数 main 的最后一个语句都还没有返回,那么编译器会隐式地插入返回 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:

关于主函数 main 返回的另一个特别之处在于如何处理它的返回值。在第 1.1 节已知,可将主函数 main 返回的值视为状态指示器。返回 0 表示程序运行成功,其他大部分返回值则表示失败。非 0 返回值的意义因机器不同而不同,为了使返回值独立于机器,cstdlib 头文件定义了两个预处理变量(第 2.9.2 节),分别用于表示程序运行成功和失败:

     #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.

我们的代码不再需要使用那些依赖于机器的精确返回值。相应地,这些值都在 cstdlib 库中定义,我们的代码不需要做任何修改。

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."

函数的返回值用于初始化在调用函数处创建的临时对象。在求解表达式时,如果需要一个地方储存其运算结果,编译器会创建一个没有命名的对象,这就是临时对象。在英语中,C++ 程序员通常用 temporary 这个术语来代替 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:

例如,下面的程序提供了一个计数器、一个单词 word 和单词结束字符串 ending,当计数器的值大于 1 时,返回该单词的复数版本:

     // 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.

这个函数要么返回其形参 word 的副本,要么返回一个未命名的临时 string 对象,这个临时对象是由字符串 wordending 的相加而产生的。这两种情况下,return 都在调用该函数的地方复制了返回的 string 对象。

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:

当函数返回引用类型时,没有复制返回值。相反,返回的是对象本身。例如,考虑下面的函数,此函数返回两个 string 类型形参中较短的那个字符串的引用:

     // 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.

形参和返回类型都是指向 const string 对象的引用,调用函数和返回结果时,都没有复制这些 string 对象。

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.

这个函数会在运行时出错,因为它返回了局部对象的引用。当函数执行完毕,字符串 ret 占用的储存空间被释放,函数返回值指向了对于这个程序来说不再有效的内存空间。

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

     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.

函数的返回类型可以是大多数类型。特别地,函数也可以返回指针类型。和返回局部对象的引用一样,返回指向局部对象的指针也是错误的。一旦函数结束,局部对象被释放,返回的指针就变成了指向不再存在的对象的悬垂指针(第 5.11 节)。

Exercises Section 7.3.2

Exercise 7.17:

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

什么时候返回引用是正确的?而什么时候返回 const 引用是正确的?

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;
     }


7.3.3. Recursion

7.3.3. 递归

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.

直接或间接调用自己的函数称为递归函数。一个简单的递归函数例子是阶乘的计算。数 n 阶乘是从 1 到 n 的乘积。例如,5 的阶乘就是 120。

     1 * 2 * 3 * 4 * 5 = 120

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.

递归函数必须定义一个终止条件;否则,函数就会“永远”递归下去,这意味着函数会一直调用自身直到程序栈耗尽。有时候,这种现象称为“无限递归错误”。对于函数 factorialval 为 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.

这个例子中,终止条件是余数为 0。如果用实参 (15, 123) 来调用 rgcd 函数,结果为 3。表 7.1 跟踪了它的执行过程。

Table 7.1. Trace of rgcd(15,123)
表 7.1. 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.

满足了终止条件,它返回最大公约数 3。该值依次成为前面每个调用的返回值。这个过程称为此值向上回渗(percolate),直到执行返回到第一次调用 rgcd 的函数。

The main function may not call itself.

主函数 main 不能调用自身。



Exercises Section 7.3.3

Exercise 7.20:

Rewrite factorial as an iterative function.

将函数 factorial 重写为迭代函数(即非递归函数)。

Exercise 7.21:

What would happen if the stopping condition in factorial were:

如是函数 factorial 的终止条件为:

     if (val != 0)

会出现什么问题?


Team LiB
Previous Section Next Section