函数参数简介
本节阅读量:
上一课中,学习了让函数将值返回给函数调用者。创建在该程序中模块化getValueFromUser函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
int getValueFromUser ()
{
std:: cout << "Enter an integer: " ;
int input{};
std:: cin >> input;
return input;
}
int main ()
{
int num { getValueFromUser() };
std:: cout << num << " doubled is: " << num * 2 << '\n' ;
return 0 ;
}
如何将输出的处理也放到函数中?尝试如下这样操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
int getValueFromUser ()
{
std:: cout << "Enter an integer: " ;
int input{};
std:: cin >> input;
return input;
}
// 这个函数无法编译通过
void printDouble ()
{
std:: cout << num << " doubled is: " << num * 2 << '\n' ;
}
int main ()
{
int num { getValueFromUser() };
printDouble();
return 0 ;
}
这无法编译通过,因为函数printDouble不认识标识符num。或者尝试在函数printDouble() 中将num定义为变量:
1
2
3
4
5
void printDouble ()
{
int num{}; // 新增一行
std:: cout << num << " doubled is: " << num * 2 << '\n' ;
}
虽然解决了编译器错误,但程序仍无法正常工作(始终打印“0 doubled is:0”)。问题的核心是函数printDouble未访问到用户输入的值。
我们需要将变量num的值传递给函数printDouble,以便printDouple在函数体中使用该值。
函数参数
函数参数是函数头中定义的变量。函数参数的工作方式与函数内定义的变量几乎相同。只一点不同:初始化是用函数调用方提供的值。
函数参数在函数头中定义,在函数名后面的括号之间,多个参数逗号分隔。
有不同数量参数的函数示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 本函数无参数
// 不依赖调用函数的任何数据
void doPrint ()
{
std:: cout << "In doPrint() \n " ;
}
// 本函数有一个参数x
// 调用函数需要提供一个值给x
void printValue (int x)
{
std:: cout << x << '\n' ;
}
// 本函数有两个参数,x与y
// 调用函数需要提供两个值,分别给x与y
int add (int x, int y)
{
return x + y;
}
参数是函数调用时从调用方传递给函数的值:
1
2
3
doPrint(); // 没有参数
printValue(6 ); // 参数是6
add(2 , 3 ); // 参数是2,3
注意,调用时多个参数由逗号分隔。
参数如何工作
调用函数时,所有参数被创建为变量,且每个值都被复制到匹配的参数中。该过程称为传递值。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
// 本函数有两个参数,x与y
// 调用函数需要提供两个值,分别给x与y
void printValues (int x, int y)
{
std:: cout << x << '\n' ;
std:: cout << y << '\n' ;
}
int main ()
{
printValues(6 , 7 ); // 函数调用,传递6,7,分别给x和y
return 0 ;
}
用参数6和7调用函数printValues时,将创建printValue的参数x并初始化为值6,创建printValues的参数y并初始化为值7。
运行结果输出:
注意,调用时传递值的数量必须与函数参数的数量匹配,否则编译器将抛出错误。传递给函数的参数可以是有效的表达式(因为传递的本质只是参数的初始值设定项,初始值设定值可以是合法的表达式的计算结果)。
修复问题代码
修复课程开始演示的程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
int getValueFromUser ()
{
std:: cout << "Enter an integer: " ;
int input{};
std:: cin >> input;
return input;
}
void printDouble (int value) // 此函数有一个value参数
{
std:: cout << value << " doubled is: " << value * 2 << '\n' ;
}
int main ()
{
int num { getValueFromUser() };
printDouble(num);
return 0 ;
}
该程序中,用户输入值初始化变量num。接着,调用函数printDouble,并将num的值复制到函数printDouble的参数中。函数printDouble使用参数value的值。
使用函数的返回值作为参数
上面问题中,变量num仅使用一次,将getValueFromUser的返回值传输到printDouble调用参数。
将上述示例简化如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
int getValueFromUser ()
{
std:: cout << "Enter an integer: " ;
int input{};
std:: cin >> input;
return input;
}
void printDouble (int value)
{
std:: cout << value << " doubled is: " << value * 2 << '\n' ;
}
int main ()
{
printDouble(getValueFromUser());
return 0 ;
}
直接用函数getValueFromUser的返回值作为函数printDouble的参数!
尽管该程序更简洁(并且清楚地表明,用户读取的值将不用于其他用途),但也会发现这种“紧凑语法”难以阅读。使用上一个含变量的版本也是可以的。
参数和返回值如何一起工作
使用参数和返回值,将数据作为输入,对其进行计算,并将值返回给调用者。
如下简单的函数示例,该函数将两个数字相加,将结果返回给调用者:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
// add() 使用2个整数作为输入,返回它们的和
// x and y 的值由调用函数决定
int add (int x, int y)
{
return x + y;
}
// main 函数没有参数
int main ()
{
std:: cout << add(4 , 5 ) << '\n' ; // 4 和 5 被传递给add
return 0 ;
}
执行从main顶部开始。当计算add(4,5)时,调用函数add,参数x用值4初始化,参数y用值5初始化。
函数add中的return语句计算x+y以产生值9,将该值返回给main。并将值9发送到std::cout,在控制台打印。
输出:
函数调用行为
更多示例
更多的函数调用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
int add (int x, int y)
{
return x + y;
}
int multiply (int z, int w)
{
return z * w;
}
int main ()
{
std:: cout << add(4 , 5 ) << '\n' ; // add() x=4, y=5, x+y=9
std:: cout << add(1 + 2 , 3 * 4 ) << '\n' ; // add() x=3, y=12, x+y=15
int a{ 5 };
std:: cout << add(a, a) << '\n' ; // (5 + 5)
std:: cout << add(1 , multiply(2 , 3 )) << '\n' ; // 1 + (2 * 3)
std:: cout << add(1 , add(2 , 3 )) << '\n' ; // 1 + (2 + 3)
return 0 ;
}
该程序生成输出:
main函数中第一个语句很简单。
在第二条语句中,参数在传递之前进行求值的表达式,1+2的计算结果为3,因此3被复制到参数x。3*4是12,因此12被复制到了参数y。add(3,12)结果为15。
下一对语句也相对简单:
1
2
int a{ 5 };
std:: cout << add(a, a) << '\n' ; // (5 + 5)
调用add(),a的值被复制到参数x和y中。a的值为5,add(a, a)=add(5, 5),结果为值10。
这组中的一个棘手的语句:
1
std:: cout << add(1 , multiply(2 , 3 )) << '\n' ; // 1 + (2 * 3)
执行函数add时,程序需要确定参数x和y的值。x很简单,将整数1传递给x。要获得参数y的值,需要先计算multiply(2, 3)。程序调用multiply并初始化z=2和w=3,multiply(2, 3)返回整数值6。返回值6用于初始化add函数的y参数。add(1, 6)返回整数7,将其传递给std::cout打印。
更明确地说:add(1, multiply(2, 3))求值为add(1, 6),计算为7
下面的语句看起来棘手,add()的参数之一是另一个add()函数。
1
std:: cout << add(1 , add(2 , 3 )) << '\n' ; // 1 + (2 + 3)
但这种情况与前面的情况完全相同。add(2,3) 首先解析,返回值为5。接着解析为add(1, 5),计算结果为值6,该值被传递给std::cout打印。
更明确地说:add(1, add(2, 3)) 求值为add(1, 5) => 结果为6
未使用的参数
在某些情况下,将遇到参数未被使用的函数。
举个小例子:
1
2
3
4
5
6
7
8
9
void doSomething (int count) // warning: 参数count未被使用
{
// 函数体中没有用到 count
}
int main ()
{
doSomething(4 );
}
就像未使用的局部变量一样,编译器会警告已定义但未使用的参数。
函数定义中,函数参数的名称是可选的。因此,在函数参数需要存在但未使用的情况下,可简单地省略名称。没有名称的参数称为未命名参数:
1
2
3
void doSomething (int ) // ok: 未命名参数
{
}
Google C++样式指南建议使用注释来记录未命名参数的名称:
1
2
3
void doSomething (int /*count*/ )
{
}
关键点
如果简单地删除未使用的函数参数,则对该函数的现有调用将编译失败(因为函数调用将提供比函数可接受更多的参数)。
结论
函数参数和返回值是让函数可重用的关键,不必知道预先输入,给出对应的计算结果。