在上一课中,我们了解到可以让函数将值返回给函数的调用者。 我们用它来创建一个在此程序中使用的模块化 getValueFromUser 函数:
#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;
}
但是,如果我们也想将输出行放入其自己的函数中怎么办?你可以尝试这样做:
#include <iostream>
int getValueFromUser()
{
std::cout << "Enter an integer: ";
int input{};
std::cin >> input;
return input;
}
// This function won't compile
void printDouble()
{
std::cout << num << " doubled is: " << num * 2 << '\n';
}
int main()
{
int num { getValueFromUser() };
printDouble();
return 0;
}
这不会编译,因为函数printDouble不知道标识符num是什么。您可以尝试将 num 定义为函数 printDouble() 内的变量:
void printDouble()
{
int num{}; // we added this line
std::cout << num << " doubled is: " << num * 2 << '\n';
}
虽然这解决了编译器错误并使程序可以编译,但程序仍然无法正常工作(它总是打印“0 doubled is: 0”)。这里问题的核心是函数printDouble无法访问用户输入的值。
我们需要某种方法将变量num的值传递给函数printDouble,以便printDouble可以在函数体中使用该值。
形式参数和实际参数
在许多情况下,能够将信息传递给正在调用的函数,以便该函数可以使用数据是很有用的。例如,如果我们想编写一个函数来添加两个数字,我们需要某种方法来告诉函数在调用它时要添加哪两个数字。否则,函数怎么知道要添加什么?我们通过函数参数和参数来做到这一点。
函数参数(形式参数)是在函数定义中声明的变量或标识符。函数参数的工作方式与函数内部定义的变量几乎相同,但有一个区别:它们使用函数调用者提供的值进行初始化。
函数参数在函数头中定义,将其放置在函数名称后面的括号之间,多个参数之间用逗号分隔。
以下是具有不同数量参数的函数的一些示例:
// 此函数不接受任何参数
// 它不依赖调用者提供任何信息
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;
}
参数是在进行函数调用时从调用者传递给函数的值:
doPrint(); // 这个调用没有参数
printValue(6); // 6 是传递给函数 printValue() 的参数
add(2, 3); //2 和 3 是传递给函数 add() 的参数
请注意,多个参数也用逗号分隔。
参数和实参如何协同工作
当调用函数时,函数的所有参数都被创建为变量,并且每个参数的值都被复制到匹配的参数中。这个过程称为按值传递。
例如:
#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
return 0;
}
当使用参数6和7调用函数printValues时,将创建printValues的参数x并使用值6进行初始化,并创建printValues的参数y并使用值7进行初始化。
这导致输出:
6
7
请注意,参数的数量通常必须与函数参数的数量匹配,否则编译器将抛出错误。传递给函数的参数可以是任何有效的表达式(因为参数本质上只是参数的初始值设定项,并且初始值设定项可以是任何有效的表达式)。
优化开头的程序
#include <iostream>
int getValueFromUser()
{
std::cout << "Enter an integer: ";
int input{};
std::cin >> input;
return input;
}
void printDouble(int value) // This function now has an integer parameter
{
std::cout << value << " doubled is: " << value * 2 << '\n';
}
int main()
{
int num { getValueFromUser() };
printDouble(num);
return 0;
}
在此程序中,变量num首先使用用户输入的值进行初始化。然后,调用函数printDouble ,并将参数num的值复制到函数printDouble的value参数中。然后函数printDouble使用参数value的值。
使用返回值作为参数
在上面的问题中,我们可以看到变量num仅使用一次,用于将函数getValueFromUser的返回值传输到调用函数printDouble的参数。
我们可以将上面的例子稍微简化如下:
#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的参数!
参数和返回值如何协同工作
通过使用参数和返回值,我们可以创建将数据作为输入的函数,对其进行一些计算,然后将值返回给调用者。
下面是一个非常简单的函数示例,它将两个数字相加并将结果返回给调用者:
#include <iostream>
// add()函数接受两个整数作为参数,并返回它们的和
// 参数x和y的值由调用add()函数的地方确定
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以打印在控制台上。
输出:
9
以图片形式:
更多示例
让我们看一下更多的函数调用:
#include <iostream>
// 定义一个函数 add(),接受两个整数参数 x 和 y,并返回它们的和
int add(int x, int y)
{
return x + y;
}
// 定义一个函数 multiply(),接受两个整数参数 z 和 w,并返回它们的乘积
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;
}
输出:
9
15
10
7
6
第一个陈述很简单。
在第二个语句中,参数是在传递之前进行计算的表达式。在本例中,1 + 2的计算结果为3,因此3被复制到参数x。3 * 4 的计算结果为12,因此12被复制到参数y。add(3, 12)解析为15。
接下来的一对语句也相对简单:
int a{ 5 };
std::cout << add(a, a) << '\n'; // evaluates (5 + 5)
在这种情况下,调用add() ,其中a的值被复制到参数x和y中。由于a 的值为5,因此add(a, a) = add(5, 5),解析为值10。
让我们再看这一句:
std::cout << add(1, multiply(2, 3)) << '\n'; // evaluates 1 + (2 * 3)
当执行函数add时,程序需要确定参数x和y的值是什么。x很简单,因为我们刚刚向它传递了整数1。要获取参数y的值,需要首先计算multip(2, 3)。程序调用multiply并初始化z = 2和w = 3,因此multiply(2, 3)返回整数值6。现在可以使用返回值6来初始化y参数添加功能。add(1, 6)返回整数7,然后将其传递给 std::cout 进行打印。
以下语句看起来很棘手,因为提供给add 的参数之一是对add 的另一次调用。
std::cout << add(1, add(2, 3)) << '\n'; // evaluates 1 + (2 + 3)
但这个案例的工作原理与之前的案例完全相同。add(2, 3) 首先解析,导致返回值5。现在它可以解析 add(1, 5),其计算结果为值6,该值被传递到 std::cout 进行打印。
未引用的参数
在某些情况下,您会遇到函数的参数未在函数体中使用的情况。这些称为未引用参数。
当函数参数曾经使用过但不再使用时,可能会发生这种情况。
举一个简单的例子:
void doSomething(int count) // 警告:未使用的参数 count
{
// 这个函数曾经用 count 做些事情,但现在不再使用它了
}
int main()
{
doSomething(4);
}
就像未使用的局部变量一样,编译器可能会警告该变量count
已定义但未使用。
如果简单地删除未使用的函数参数,则对该函数的任何现有调用都将中断(因为函数调用将提供比函数可以接受的参数更多的参数)。
在函数定义中,函数参数的名称是可选的。因此,当函数参数需要存在但函数体中没有使用时,可以简单地省略名称。没有名称的参数称为未命名参数:
void doSomething(int) // ok: unnamed parameter will not generate warning
{
}
建议使用注释来记录未命名参数的内容:
void doSomething(int /*count*/)
{
}
当函数参数存在但未在函数体中使用时,请勿为其指定名称。您可以选择在注释中输入名称。
原创文章,作者:jkhxw,如若转载,请注明出处:https://www.jkhxw.com/cpp-function-parameters-and-arguments/