先来看个简单的问题:
Steve 新建造了一个海底农场,专用于大规模地种植海带,用以生产绿色、快捷的干海带块燃料。每个干海带块都需要 9 片海带进行生产。请问,已知他此次收获的海带数量,他最多可以加工出多少个干海带块?
不难看出,这其实也就是要我们作一次除法题,直接给出总数除以 9 后去掉小数部分的结果就好了。
// ...
int main()
{
int kelps; // 储存海带数量的变量
cin >> kelps; // 接受海带数量
int dry_kelp_blocks = kelps / 9; // 计算干海带块的数量并存储
cout << dry_kelp_blocks << endl; // 输出干海带块的数量
return 0;
}
不过,事情并没有就此结束:
Steve 卖干海带块赚了一大把绿宝石,而现在他想把绿宝石压制成块储存起来。9 个干海带块换得一块绿宝石,且 9 块绿宝石可以压制为一个绿宝石块。他想知道,已知有多少个干海带块,求他最终能获得多少个绿宝石块?
Steve 用大把的绿宝石块买了一个铁傀儡刷怪塔,现在他又有了大把大把的铁锭,他想将铁锭也压制成块进行存储。9 个铁锭可以合成一个铁块。已知铁锭的数量,求能支撑多少个铁块?
也许您会想到再写一些除法算式来解决问题,不过,重复写同样的语句是很烦人的事。有没有更简洁的办法呢?
答案是肯定的,解决方案便是将算式写成一个函数:
int make(int n)
{
return n / 9;
}
这是什么东西?看着有点像先前的main()
啊?
我们先别急着理解它,而是看看开头那段程序是怎么用函数“改进”的:
int main()
{
int kelps;
cin >> kelps;
int dry_kelp_blocks = make(kelps); // 注意这里,kelps / 9 被换掉了
cout << dry_kelp_blocks << endl;
return 0;
}
原本的kelps / 9
被替换成了make(kelps)
后,程序仍然能像之前那样输出正确的结果,而之前称之为函数的莫名其妙的一段程序也有make(int n)
和n / 9
的字样。者之间死于有种关联,使得编译器把make(kelps)
当成了kelps / 9
来解释。
实际上,刚刚的make(int n)
就是一个函数。函数(function)本质上就是一段被命名了的代码块,可以被重复执行,且无需重复去写一样的代码块。它可以接受 0 个或多个参数,并且它可以选择返回一个结果数值。
听起来有些云里雾里,不太容易理解。我们可以用这张图来描述它到底是干什么的:
图
当使用make(kelps)
时,kelps
储存的值(假设为233
)被传进了名为make
的函数中,而make
将传进来的这个值赋予了新的名字n
。此时在函数内部便多了个局部变量n
,其储存的值正是刚刚传过来的233
。这个函数会对局部变量n
与数字 9 相除,得到的结果便代表着make(kelps)
的值。kelps
在这里被称作实参,是实际参数的简称;而在这个函数开头定义的n
在这里被称作形参,是形式参数的坚称。这个函数对局部函数n
做出计算后,将结果值以return
语句返回给了使用make(kelps)
位置的表达式,这个结果值也被称作返回值。
我们无需立即就去理解透彻实参、形参和返回值的概念,现在首要的任务是先搞懂函数究竟是什么,以及为什么我们要使用它、它能为我们带来什么。
前面我们有了解到,函数其实就是一段有名字的程序,我们可以在其它函数(包括main
,它本质上其实也就是一个函数)内部使用它们,而且可以重复地使用。既然可以重复地使用,我们便可以将需要执行多次地程序段写成一个函数,这样便不需要费事地重复写一样的程序了。
我们会在未来学习更多用于减少代码量的“偷懒”方法,而要理解更多的“偷懒”方法,必须先理解函数。更具体的剖析,将在下一章详细讲解。
在本章的最后,我们将 Steve 的额外问题解决一下吧。下面是计算绿宝石块数量的程序:
#include <iostream>
using namespace std;
int make(int n) // 接受一个参数 n,返回一个类型为 int 的值
{
return n / 9; // 返回 n 除以 9 取整的结果
}
int main() // 整个程序的入口
{
cout << "请输入干海带块的数量: ";
int n;
cin >> n;
// 思考一下,make(make(n)) 是什么意思?
cout << endl << "绿宝石块的数量为 " << make(make(n)) << endl;
return 0;
}
这是我们第一次完整地写一次带函数地程序。值得注意地是,函数make
是写在函数main
之前的,这里就需要提到初学者很容易栽的坑之一了————函数在使用前必须声明。
什么意思呢?我们先试试把make
拿到·main
后面试试:
// ...
int main()
{
// ...
}
int make(int n)
{
// ...
}
将修改后的程序交给编译器,它会告诉你找不到哪里有什么make
函数。
很奇怪吧?因为编译器很“笨”,它不会先把整个程序通读一遍,而是老老实实一个字一个字地读。换句话说,如果把make
函数写在main
函数后头,编译器在解决掉main
函数之前,是根本不会“感受”到main
函数存在的。
那怎么才能在将make
函数放在main
函数后时,能让编译器正确识别呢?答案很简单,在main
函数之前和编译器先打声招呼就好了:
// ...
int make(int); // 在使用前声明一个尚未具体定义的函数
int main()
{
// ...
}
int make(int n)
{
// ...
}
这个声明基本上与定义函数的格式一致,不过后面以花括号括起的代码段被替换为了一个分号,以表示这仅仅是事先声明有这个函数而已,定义的事情“以后再说”。
下面两种声明语法是等价的————在函数声明中,您可以不为参数命名:
int make(int);
int make(int n);