C++ 自学教程 第1.4章 初探函数和返回值
函数(function)是一系列可被重复使用的叙述语句,通常被设计用来完成某项特定工作。你已经知道每个程序必须包含一个主函数main(程序从它开始执行)。但是,大部分程序使用更多的函数。
一般来说,你的程序需要暂时“放下手里的工作”,去完成一些别的事。我们在现实生活中也常常这样。比方说,你在读一本书的时候突然想起来自己有个电话要打。你会在书本里加一张书签,打电话,打完了之后再度打开书,继续阅读。
C++程序也是这样运行。程序会依次执行函数里的一条条叙述,直到碰到一条函数调用。函数调用(function call)是一个表达式,它告诉CPU暂时中止现在的函数并执行另一个函数。CPU会“加个书签”:记住当前执行位置,然后执行函数调用中调用的函数。当这个被调用的函数被执行完毕后,CPU回到标记位置,继续执行原函数。
发起函数调用的函数是呼叫函数(caller),被调用的函数是被调用函数(called)。
下面给出一个新函数如何被定义以及调用的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//#include "stdafx.h" // Visual Studio users need to uncomment this line
#include <iostream> // for std::cout and std::endl
// Definition of function doPrint()
//定义函数doPrint
void doPrint() // doPrint() is the called function in this example
{
std::cout << "In doPrint()" << std::endl;
}
// Definition of function main()
//定义主函数
int main()
{
std::cout << "Starting main()" << std::endl;
// Interrupt main() by making a function call to doPrint(). main() is the caller.
//打断主函数,调用函数doPrint
doPrint();
std::cout << "Ending main()" << std::endl;
return 0;
}
这个程序的输出结果是:
Starting main()
In doPrint()
Ending main()
这个程序从主函数第一行开始执行,输出了‘Starting main()’。主函数的第二行是个函数调用:doPrint。这时,主函数的执行被中断,CPU跳转到doPrint() 执行并输出‘In doPrint()’。当doPrint执行完毕,呼叫函数(主函数)回到刚刚的中止位置,执行下一行叙述:输出‘Ending main ()’。
注意函数调用使用了函数名加上一对括号()。下一节会进一步探讨这对括号的使用。今天,你只需要记住如果你忘记加这对括号你的程序可能不能编译。
规则:调用函数时别忘记加括号。
返回值
当主函数执行完毕,它通过返回叙述向操作系统返回了一个整数(通常是0)。
当你编写自己的函数的时候,你可以自己决定这个函数是否向呼叫函数返回数值。这类操作通过设置函数的返回值类型(return type)来实现。返回值类型被放在函数定义的一开始。注意返回值类型只限定了返回值的类型,而没有给出具体的数值。
然后,我们在被调用函数里面使用返回叙述向呼叫函数提供具体的返回值。这个数值被称为返回值(return value)。
下面让我们来看看一个使用返回值的具体例子:
// int means the function returns an integer value to the caller
int return5()
{
// this function returns an integer, so a return statement is needed
return 5; // we're going to return integer value 5 back to the caller of this function
}
int main()
{
std::cout << return5() << std::endl; // prints 5
std::cout << return5() + 2 << std::endl; // prints 7
return5(); // okay: the value 5 is returned, but is ignored since main() doesn't do anything with it
return 0;
}
在第一次对return5() 的函数调用中,被调用函数向呼叫函数返回了数值5。 这个数值后被传送给了std::cout输出在屏幕上。
在第二次对return5() 的函数调用中,被调用函数同样返回数值5。表达式5+2被估值为7.最后数值7被传递给了输出。
在第三次对return5() 的函数调用中,被调用函数同样返回数值5。但是主函数并没有使用这个数值,所以后面什么都没发生(这个返回值被忽略了)。
注意,除非使用std::cout输出,返回值并不会显示在屏幕上。在上面第三个例子里,返回值就没有被输出。
返回值类型为空
函数并不是必须返回一个数值的。想要告诉编译器这个函数没有返回值,将它的返回值设置为空(void)就可以了。让我们来看看上面给出的doPrint()例子:
void doPrint() // void is the return type
{
std::cout << "In doPrint()" << std::endl;
// This function does not return a value so no return statement is needed
}
这个函数的返回值类型是空,也就是说它不向呼叫函数返回数值。而因为它不返回数值,这个函数内也不需要使用返回叙述。
下面给了另一个返回值类型为空的函数例子:
// void means the function does not return a value to the caller
void returnNothing()
{
std::cout << "Hi" << std::endl;
// This function does not return a value so no return statement is needed
}
int main()
{
returnNothing(); // okay: function returnNothing() is called, no value is returned
std::cout << returnNothing(); // error: this line will not compile. You'll need to comment it out to continue.
return 0;
}
在第一个对returnNothing()的函数调用中,函数输出了‘Hi’,并不向呼叫函数返回任何数值。然后CPU回到原来的主函数,并继续执行。
第二次对returnNothing()的函数调用甚至无法被编译。函数的返回值类型为空,也就是说它不应该有返回值。但是主函数却试图把“空”发送给std::cout输出。而std::cout无法处理“空”值,编译器陷入了懵逼(这时候输出什么呢?)所以编译器会报错。你必须把这一行注释掉才能运行你的程序。
我们通常在不需要被调用函数返回任何东西的时候才会将它的返回值类型为空。在上面这个例子returnNothing()函数有自己的功能:输出‘Hi’,但它不需要向主函数返回任何数值。因此,我们把的返回值类型设置为空。
返回主函数
你现在对主函数怎么运行有个大概的概念了。当程序运行时,操作系统调用了主函数,从它的第一行开始执行。主函数的叙述语句被从上到下依次执行。最终,主函数反悔了一个整数值(通常是0)。这也解释了为什么主函数的返回值类型是int。
为什么要向操作系统返回一个数值呢?这个数值也叫做状态码(status code)告诉了操作系统你的函数是否执行成功了。默认情况下返回值是0的时候意思是运行成功,是正数的时候意思是运行失败。
注意C++标准明确的说了主函数必须返回一个整数。但是,如果你在主函数里没有给出返回叙述,编译器会替你返回一个0.虽然这么说,最好还是明确向主函数返回一个数值,即表露了你的意图,又与其他函数保持一致。
在这个跟阶段,你应该在你的代码最后定义你的主函数。后面1.7节会再讨论这个问题。
关于返回值的其他几个点
首先,如果一个函数返回值为类型非空,它返回的数值必须是该类型。唯一的例外就是主函数,如果不提供返回值会默认返回0。
其次,当函数执行到了它的返回叙述,被调用函数会马上在这一步回到呼叫函数。余下的所有其余代码都被跳过。
一个函数只能向呼叫函数返回一个数值。但是,函数本身会自己具体返回什么数值。它可能返回一个单个数值(比如5)。它也可能回复一个变量或者表达式,或者也可能从一些可能的数值里面挑一个返回。
也有方法可以返回多个数值,我们之后会深入讨论。
最后,函数可以自己决定它的返回值的意义。一些函数使用返回值表达状态值,来暗示函数是否成功执行。其他函数返回一个计算得到的数字或者被挑选的数字。还有些函数什么都不返回。函数返回什么,和返回值的意思由作者自己决定。因为什么样的情况都有可能,所以最好给你的函数注释一下说明你的函数返回值是什么意思。
重复使用函数
同一个函数可以被多次调用。当你需要重复完成一个任务的时候这一点特别有用。
//#include "stdafx.h" // Visual Studio users need to uncomment this line
#include <iostream>
// getValueFromUser will read a value in from the user, and return it to the caller
int getValueFromUser()
{
std::cout << "Enter an integer: "; // ask user for an integer
int a; // allocate a variable to hold the user input
std::cin >> a; // get user input from console and store in variable a
return a; // return this value to the function's caller (main)
}
int main()
{
int x = getValueFromUser(); // first call to getValueFromUser
int y = getValueFromUser(); // second call to getValueFromUser
std::cout << x << " + " << y << " = " << x + y << std::endl;
return 0;
}
这个函数会输出以下内容:
Enter an integer: 5
Enter an integer: 7
5 + 7 = 12
在这个例子里面,主函数被打断了两次执行getValueFromUser()函数。注意每一次变量a读取的数值都被返回给了主函数,并被赋值给了变量x或者y!
不是只有主函数可以调用其他函数,任何函数都可以调用别的函数!
//#include "stdafx.h" // Visual Studio users need to uncomment this line
#include <iostream>
void printA()
{
std::cout << "A" << std::endl;
}
void printB()
{
std::cout << "B" << std::endl;
}
// function printAB() calls both printA() and printB()
void printAB()
{
printA();
printB();
}
// Definition of main()
int main()
{
std::cout << "Starting main()" << std::endl;
printAB();
std::cout << "Ending main()" << std::endl;
return 0;
}
这个函数会输出以下内容:
Starting main()
A
B
Ending main()
嵌套函数
在C++里,函数不能在别的函数内被定义。下面这个例子就不符合语法规范:
#include <iostream>
int main()
{
void foo() // this function is nested inside main(), which is illegal.
{
std::cout << "foo!" << std::endl;
}
foo();
return 0;
}
正确的的编写这个函数的方法是:
#include <iostream>
void foo() // no longer inside of main()
{
std::cout << "foo!" << std::endl;
}
int main()
{
foo();
return 0;
}
小测验时间~
浏览下面的代码,并回答他们的输出是什么,或者是否能被编译
1a)
#include <iostream>
int return7()
{
return 7;
}
int return9()
{
return 9;
}
int main()
{
std::cout << return7() + return9() << std::endl;
return 0;
}
1b)
#include <iostream>
int return7()
{
return 7;
int return9()
{
return 9;
}
}
int main()
{
std::cout << return7() + return9() << std::endl;
return 0;
}
1c)
#include <iostream>
int return7()
{
return 7;
}
int return9()
{
return 9;
}
int main()
{
return7();
return9();
return 0;
}
1d)
#include <iostream>
void printA()
{
std::cout << "A" << std::endl;
}
void printB()
{
std::cout << "B" << std::endl;
}
int main()
{
printA();
printB();
return 0;
}
1e)
#include <iostream>
void printA()
{
std::cout << "A" << std::endl;
}
int main()
{
std::cout << printA() << std::endl;
return 0;
}
1f)
#include <iostream>
int getNumbers()
{
return 5;
return 7;
}
int main()
{
std::cout << getNumbers() << std::endl;
std::cout << getNumbers() << std::endl;
return 0;
}
1g)
#include <iostream>
int return 5()
{
return 5;
}
int main()
{
std::cout << return 5() << std::endl;
return 0;
}
1h)
#include <iostream>
int return5()
{
return 5;
}
int main()
{
std::cout << return5 << std::endl;
return 0;
}
答案:
a) 输出16
b)这段程序不会编译,函数不能嵌套
c) 可以被编译,但是没有输出。
d) 依次输出A和B
e) 这段程序不能编译,函数printA()返回空值,但是主函数却要把空值发送给std::cout.
f) 这段程序会在上下两行分别输出5。当函数getNumbers()被调用,数值5被返回。当返回叙述被执行后,被调用函数马上终止执行。所以下面一行返回数值7不会被执行。
g)无法编译,函数名无效。
h) 函数可以被编译,但是少了一对括号所以被调用函数不会被调用,具体会得到什么结果取决于编译器。
说明: 这系列笔记是基于网上一个英文教程LearnCPP