Team LiB
Previous Section Next Section

7.1. Defining a Function

7.1. 函数的定义

A function is uniquely represented by a name and a set of operand types. Its operands, referred to as parameters, are specified in a comma-separated list enclosed in parentheses. The actions that the function performs are specified in a block, referred to as the function body. Every function has an associated return type.

函数由函数名以及一组操作数类型唯一地表示。函数的操作数,也即形参,在一对圆括号中声明,形参与形参之间以逗号分隔。函数执行的运算在一个称为函数体的块语句中定义。每一个函数都有一个相关联的返回类型

As an example, we could write the following function to find the greatest common divisor of two ints:

考虑下面的例子,这个函数用来求出两个 int 型数的最大公约数:

     // return the greatest common divisor
     int gcd(int v1, int v2)
     {
         while (v2) {
             int temp = v2;
             v2 = v1 % v2;
             v1 = temp;
         }
         return v1;
     }

Here we define a function named gcd that returns an int and has two int parameters. To call gcd, we must supply two int values and we get an int in return.

这里,定义了一个名为 gcd 的函数,该函数返回一个 int 型值,并带有两个 int 型形参。调用 gcd 函数时,必须提供两个 int 型值传递给函数,然后将得到一个 int 型的返回值。

Calling a Function

函数的调用

To invoke a function we use the call operator, which is a pair of parentheses. As with any operator, the call operator takes operands and yields a result. The operands to the call operator are the name of the function and a (possibly empty) comma-separated list of arguments. The result type of a call is the return type of the called function, and the result itself is the value returned by the function:

C++ 语言使用调用操作符(即一对圆括号)实现函数的调用。正如其他操作符一样,调用操作符需要操作数并产生一个结果。调用操作符的操作数是函数名和一组(有可能是空的)由逗号分隔的实参。函数调用的结果类型就是函数返回值的类型,该运算的结果本身就是函数的返回值:

     // get values from standard input
     cout << "Enter two values: \n";
     int i, j;
     cin >> i >> j;
     // call gcd on arguments i and j
     // and print their greatest common divisor
     cout << "gcd: " << gcd(i, j) << endl;

If we gave this program 15 and 123 as input, the output would be 3.

如果给定 15 和 123 作为程序的输入,程序将输出 3。

Calling a function does two things: It initializes the function parameters from the corresponding arguments and transfers control to the function being invoked. Execution of the calling function is suspended and execution of the called function begins. Execution of a function begins with the (implicit) definition and initialization of its parameters. That is, when we invoke gcd, the first thing that happens is that variables of type int named v1 and v2 are created. These variables are initialized with the values passed in the call to gcd. In this case, v1 is initialized by the value of i and v2 by the value of j.

函数调用做了两件事情:用对应的实参初始化函数的形参,并将控制权转移给被调用函数。主调函数的执行被挂起,被调函数开始执行。函数的运行以形参的(隐式)定义和初始化开始。也就是说,当我们调用 gcd 时,第一件事就是创建名为 v1v2int 型变量,并将这两个变量初始化为调用 gcd 时传递的实参值。在上例中,v1 的初值为 i,而 v2 则初始化为 j 的值。

Function Body Is a Scope

函数体是一个作用域

The body of a function is a statement block, which defines the function's operation. As usual, the block is enclosed by a pair of curly braces and hence forms a new scope. As with any block, the body of a function can define variables. Names defined inside a function body are accessible only within the function itself. Such variables are referred to as local variables. They are "local" to that function; their names are visible only in the scope of the function. They exist only while the function is executing. Section 7.5 (p. 254) covers local variables in more detail.

函数体是一个语句块,定义了函数的具体操作。通常,这个块语句包含在一对花括号中,形成了一个新的作用域。和其他的块语句一样,在函数体中可以定义变量。在函数体内定义的变量只在该函数中才可以访问。这种变量称为局部变量,它们相对于定义它们的函数而言是“局部”的,其名字只能在该函数的作用域中可见。这种变量只在函数运行时存在。第 7.5 节将详细讨论局部变量。

Execution completes when a return statement is encountered. When the called function finishes, it yields as its result the value specified in the return statement. After the return is executed, the suspended, calling function resumes execution at the point of the call. It uses the return value as the result of evaluating the call operator and continues processing whatever remains of the statement in which the call was performed.

当执行到 return 语句时,函数调用结束。被调用的函数完成时,将产生一个在 return 语句中指定的结果值。执行 return 语句后,被挂起的主调函数在调用处恢复执行,并将函数的返回值用作求解调用操作符的结果,继续处理在执行调用的语句中所剩余的工作。

Parameters and Arguments

形参和实参

Like local variables, the parameters of a function provide named, local storage for use by the function. The difference is that parameters are defined inside the function's parameter list and are initialized by arguments passed to the function when the function is called.

类似于局部变量,函数的形参为函数提供了已命名的局部存储空间。它们之间的差别在于形参是在函数的形参表中定义的,并由调用函数时传递函数的实参初始化。

An argument is an expression. It might be a variable, a literal constant or an expression involving one or more operators. We must pass exactly the same number of arguments as the function has parameters. The type of each argument must match the corresponding parameter in the same way that the type of an initializer must match the type of the object it initializes: The argument must have the same type or have a type that can be implicitly converted (Section 5.12, p. 178) to the parameter type. We'll cover how arguments match a parameter in detail in Section 7.8.2 (p. 269).

An argument is an expression. It might be a variable, a literal constant or an expression involving one or more operators. We must pass exactly the same number of arguments as the function has parameters. The type of each argument must match the corresponding parameter in the same way that the type of an initializer must match the type of the object it initializes: The argument must have the same type or have a type that can be implicitly converted (Section 5.12, p. 178) to the parameter type. We'll cover how arguments match a parameter in detail in Section 7.8.2 (p. 269).

实参则是一个表达式。它可以是变量或字面值常量,甚至是包含一个或几个操作符的表达式。在调用函数时,所传递的实参个数必须与函数的形参个数完全相同。与初始化式的类型必须与初始化对象的类型匹配一样,实参的类型也必须与其对应形参的类型完全匹配:实参必须具有与形参类型相同、或者能隐式转换(第 5.12 节)为形参类型的数据类型。本章第 7.8.2 节将详细讨论实参与形参的匹配。

7.1.1. Function Return Type

7.1.1. 函数返回类型

The return type of a function can be a built-in type, such as int or double, a class type, or a compound type, such as int& or string*. A return type also can be void, which means that the function does not return a value. The following are example definitions of possible function return types:

函数的返回类型可以是内置类型(如 int 或者 double)、类类型或复合类型(如 int&string*),还可以是 void 类型,表示该函数不返回任何值。下面的例子列出了一些可能的函数返回类型:

     bool is_present(int *, int);       // returns bool
     int count(const string &, char);   // returns int
     Date &calendar(const char*);       // returns reference to Date
     void process();                    // process does not return a value

A function may not return another function or a built-in array type. Instead, the function may return a pointer to the function or to a pointer to an element in the array:

函数不能返回另一个函数或者内置数组类型,但可以返回指向函数的指针,或指向数组元素的指针的指针:

     // ok: pointer to first element of the array
     int *foo_bar() { /* ... */ }

This function returns a pointer to int and that pointer could point to an element in an array.

这个函数返回一个 int 型指针,该指针可以指向数组中的一个元素。

We'll learn about function pointers in Section 7.9 (p. 276).

第 7.9 节将介绍有关函数指针的内容。

Functions Must Specify a Return Type
函数必须指定返回类型

It is illegal to define or declare a function without an explicit return type:

在定义或声明函数时,没有显式指定返回类型是不合法的:

     // error: missing return type
     test(double v1, double v2) { /* ... */ }

Eariler versions of C++ would accept this program and implicitly define the return type of test as an int. Under Standard C++, this program is an error.

早期的 C++ 版本可以接受这样的程序,将 test 函数的返回类型隐式地定义为 int 型。但在标准 C++ 中,上述程序则是错误的。

In pre-Standard C++, a function without an explicit return type was assumed to return an int. C++ programs compiled under earlier, non-standard compilers may still contain functions that implicitly return int.

在 C++ 标准化之前,如果缺少显式返回类型,函数的返回值将被假定为 int 型。早期未标准化的 C++ 编译器所编译的程序可能依然含有隐式返回 int 型的函数。



7.1.2. Function Parameter List

7.1.2. 函数形参表

The parameter list of a function can be empty but cannot be omitted. A function with no parameters can be written either with an empty parameter list or a parameter list containing the single keyword void. For example, the following declarations of process are equivalent:

函数形参表可以为空,但不能省略。没有任何形参的函数可以用空形参表或含有单个关键字 void 的形参表来表示。例如,下面关于 process 的声明是等价的:

     void process() { /* ... */ }      // implicit void parameter list

     void process(void){ /* ... */ }  // equivalent declaration

A parameter list consists of a comma-separated list of parameter types and (optional) parameter names. Even when the types of two parameters are the same, the type must be repeated:

形参表由一系列用逗号分隔的参数类型和(可选的)参数名组成。如果两个参数具有相同的类型,则其类型必须重复声明:

     int manip(int v1, v2) { /* ... */ }      // error
     int manip(int v1, int v2) { /* ... */ }  // ok

No two parameters can have the same name. Similarly, a variable local to a function may not use the same name as the name of any of the function's parameters.

参数表中不能出现同名的参数。类似地,局部于函数的变量也不能使用与函数的任意参数相同的名字。

Names are optional, but in a function definition, normally all parameters are named. A parameter must be named to be used.

参数名是可选的,但在函数定义中,通常所有参数都要命名。参数必须在命名后才能使用。

Parameter Type-Checking
参数类型检查

C++ is a statically typed language (Section 2.3, p. 44). The arguments of every call are checked during compilation.

C++ 是一种静态强类型语句(第 2.3 节),对于每一次的函数调用,编译时都会检查其实参。



When we call a function, the type of each argument must be either the same type as the corresponding parameter or a type that can be converted (Section 5.12, p. 178) to that type. The function's parameter list provides the compiler with the type information needed to check the arguments. For example, the function gcd, which we defined on page 226, takes two parameters of type int:

调用函数时,对于每一个实参,其类型都必须与对应的形参类型相同,或具有可被转换(第 5.12 节)为该形参类型的类型。函数的形参表为编译器提供了检查实参需要的类型信息。例如,第 7.1 节定义的 gcd 函数有两个 int 型的形参:

     gcd("hello", "world"); // error: wrong argument types
     gcd(24312);            // error: too few arguments
     gcd(42, 10, 0);        // error: too many arguments

Each of these calls is a compile-time error. In the first call, the arguments are of type const char*. There is no conversion from const char* to int, so the call is illegal. In the second and third calls, gcd is passed the wrong number of arguments. The function must be called with two arguments; it is an error to call it with any other number.

以上所有的调用都会导致编译时的错误。在第一个调用中,实参的类型都是 const char*,这种类型无法转换为 int 型,因此该调用不合法。而第二和第三个调用传递的实参数量有误。在调用该函数时必须提供两个实参,实参数太多或太少都是不合法的。

But what happens if the call supplies two arguments of type double? Is this call legal?

如果两个实参都是 double 类型,又会怎样呢?调用是否合法?

     gcd(3.14, 6.29);      // ok: arguments are converted to int

In C++, the answer is yes; the call is legal. In Section 5.12.1 (p. 179) we saw that a value of type double can be converted to a value of type int. This call involves such a conversionwe want to use double values to initialize int objects. Therefore, flagging the call as an error would be too severe. Rather, the arguments are implicitly converted to int (tHRough truncation). Because this conversion might lose precision, most compilers will issue a warning. In this case, the call becomes

在 C++中,答案是肯定的:该调用合法!正如第 5.12.1 节所示,double 型的值可以转换为 int 型的值。本例中的函数调用正涉及了这种转换——用 double 型的值来初始化 int 型对象。因此,把该调用标记为不合法未免过于严格。更确切地说,(通过截断)double 型实参被隐式地转换为 int 型。由于这样的转换可能会导致精度损失,大多数编译器都会给出警告。对于本例,该调用实际上变为:

     gcd(3, 6);

and returns a value of 3.

返回值是 3。

A call that passes too many arguments, omits an argument, or passes an argument of the wrong type almost certainly would result in serious run-time errors. Catching these so-called interface errors at compile time greatly reduces the compile-debug-test cycle for large programs.

调用函数时传递过多的实参、忽略某个实参或者传递错误类型的实参,几乎肯定会导致严重的运行时错误!对于大程序,在编译时检查出这些所谓的接口错误(interface error),将会大大地缩短“编译-调试-测试”的周期。

Exercises Section 7.1.2

Exercise 7.1:

What is the difference between a parameter and an argument?

形参和实参有什么区别?

Exercise 7.2:

Indicate which of the following functions are in error and why. Suggest how you might correct the problems.

下列哪些函数是错误的?为什么?请给出修改意见。

     (a) int f() {
             string s;
             // ...
             return s;
         }
     (b) f2(int i) { /* ... */ }
     (c) int calc(int v1, int v1) /* ... */ }
     (d) double square(double x) return x * x;

Exercise 7.3:

Write a program to take two int parameters and generate the result of raising the first parameter to the power of the second. Write a program to call your function passing it two ints. Verify the result.

编写一个带有两个 int 型形参的函数,产生第一个参数的第二个参数次幂的值。编写程序传递两个 int 数值调用该函数,请检验其结果。

Exercise 7.4:

Write a program to return the absolute value of its parameter.

编写一个函数,返回其形参的绝对值。


Team LiB
Previous Section Next Section