7.1. Defining a Function7.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 时,第一件事就是创建名为 v1 和 v2 的 int 型变量,并将这两个变量初始化为调用 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 Type7.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++ 中,上述程序则是错误的。
7.1.2. Function Parameter List7.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参数类型检查
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),将会大大地缩短“编译-调试-测试”的周期。 |