0?wx_fmt=jpeg


内容简介

1、第一部分第七课函数效应,分而治之

2、第一部分第八课预告:传值引用,文件源头


函数效应,分而治之

上一课《》中,我们学习了条件语句和循环语句。

这两种语句也算是算法的核心了。在更早的课程中,我们学习了变量。这些都是所有编程语言的必备元素。

这一课我们又要学习一个几乎所有编程语言都有的极重要元素:

函数

C++的所有程序都或多或少用到函数,到目前为止,你其实也已经用了好多次了,不过你可能只缘身在此山中,还云深不知处。

函数的功用是:

将程序切分成更小的可重用的单元,有点像砖块。一旦砖块制作完毕,程序员所要做的就是用砖块来造东西。

我们砌一堵墙,盖一座小屋,或者摩天大楼,都需要不少砖块。用函数构建我们的程序就似用砖块搭建,而且如果拆除了,之后这些砖块还可以重复使用。

慢慢你就能体会了。编程之美是需要时间去印证的,如果只远观而不"亵玩",没有动手实验,是不能进步的。

首先我们就挽起袖子,和好水泥,来学习如何制作"砖块"(函数)吧。


函数的创建和使用

从这个课程的最初,我们已经使用了函数了。而且到目前为止都是那个函数:main函数(是我,是我,还是我...)。

这是C++程序的入口,程序是从main函数开始执行的。如下所示:

#include 
using namespace std;int main() // main函数开始,程序的开始{  cout << "Hello World !" << endl;  return 0;} // main函数的结束,程序也随之结束

以上的程序实际是从第四行开始,在第八行结束。也就是说,此程序的所有动作都是在一个函数里完成的。我们并没有跳转到其他地方,没有出这个main函数,而是按顺序执行main函数中的各句指令。

既然我这样说,聪明如你应该料想到了:我们可以写其他的函数,把一个复杂的程序分割成相对独立的区块。

是的。但是为什么要这么做呢?

虽然说,把所有代码都放在一个main函数里是完全可以的,但这并不是一个好习惯。

假设我们要开发一个大型3D游戏。

大型3D游戏可是很复杂的,代码数动辄上万行。如果我们把这么多代码都一股脑儿放到main函数里,那么是很容易迷失在茫茫码海中的。

比较理想的方式是在某个地方存放一小块代码,比如用于移动人物;另一个地方存放另一块代码,用于加载关卡,等等。

把代码分成函数可以方便管理,使我们的代码更容易被别人理解,我们自己回看时也不至于迷失了方向。

而且,假如你们是好几个程序员一起协同开发,那么函数可以让你们更好地分配工作,比如某个人负责哪些功能开发,就需要写哪些函数。

但这并不是函数的全部益处。

举个例子:我们要计算平方根。上一课我们已经学习了怎么做,我们使用了数学库中的sqrt这个函数。

类似开平方根这样常用的代码块,如果包装成一个函数,那么在不同地方使用时就不需要把相同的代码重新拷贝一次了,只需要写一下函数名就可以了。

所以函数使我们可以重复使用已有的代码块,极大地提高效率。

函数简介

函数包含完成特定任务的一个代码块。它接收需要处理的数据,处理之,然后返回一个值。

可以把函数想象成一台制作香肠的机器,在输入那一头你把猪装进去,输出那一头就出来香肠了。这酸爽。

0?wx_fmt=jpeg

输入函数的数据称为参数,而函数输出的数据称为返回值。如下图所示:

0?wx_fmt=jpeg

你是否还记得我们上一课列出的多个数学函数中的pow函数,是用于计算次方值的。例如2的3次方,可以用pow(2, 3)来计算。

如果我们使用刚才所说的专业术语(参数,返回值),那么pow函数的基本介绍可以如下所示:

  1. 接收两个参数x和y

  2. 进行数学计算(x的y次方)

  3. 返回值就是计算结果

如下图所示:

0?wx_fmt=jpeg

定义函数

好了,该放手一搏了。上面看了一些函数的例子,我们自己来定义一个函数吧。

首先,C++中所有的函数基本都遵循以下模板:

type functionName(arguments)

{
    // Body : Instructions
}

上面的是英语的表述法,如果翻成中文就是:

类型  函数名(参数)

{

    // 函数体:指令

}

关于这个模板我们需要掌握四点:

  1. 函 数类型:对应输出类型,也可以把其看做函数的类型。和变量类似,函数也有类型,这类型取决于函数返回值的类型。如果一个函数返回一个浮点数(带小数点 的),那么自然我们会将函数类型定为float或者double;如果返回整数,那么我们一般会将类型定为int或long。但是我们也可以创建不返回任 何值的函数。

  2. 函数名:这是你的函数的名字。我们已经见过不少函数名了,比如main,sqrt,pow。你可以给你的函数起任意名字,只要遵从给变量命名的相同的规则就好。

  3. 函数的参数(对应输入):参数位于函数名之后的圆括号内。这些参数是函数要用来做操作(运算)的数据。你可以给函数传入任意数量的参数,也可以不传入任何参数(例如main函数)。

  4. 函数体:大括号规定了函数的起始和结束范围。在大括号中你可以写入任意多的指令。

注意

在 C语言中,同一个程序里不允许同名的函数。但是C++中允许有同名函数,称为函数重载。只需要它们的参数不同(注意是参数不同,如果返回值不同而参数一样 不是函数重载,也不能通过编译)。例如在C++中,int multiplication(int a, int b) 和 double multiplication(double c, double d) 就是不同的函数,尽管它们的函数名一样,但是参数列表不一样。但是如果定义两个函数如下:int multiplication(int a, int b) 和 double multiplication(int c, int d),编译是会出错的,因为它们的参数一样,只是返回值不一样,是不能算不同函数的。

我们自己来定义一个函数:

int addTwo(int number){    int value(number + 2);    // 在内存中申请一个int类型的"抽屉",起名叫value    // 将参数中接收到的number进行加2操作    // 将加法的结果存入value里面        return value;    // 指定value为函数的返回值}

注意:在参数的那个括号和函数体的大括号后面都没有分号哦!

分析此函数

根据我们之前的解释,你应该明白第一行是干什么了吧。就是定义一个函数,名叫addTwo(英语"加2"的意思),接收一个int型参数作为输入,操作结束之后会返回一个int型值。如下图:

0?wx_fmt=jpeg

return value; 那一行是什么意思呢?

return是英语"返回"的意思,所以这一行指令的作用就是将value作为此函数的返回值返回。addTwo的参数number就类似之前那个图中放入香肠制造机的猪,value就类似输出的香肠。

value的类型必须是int,因为在函数定义的第一行的返回值类型是int。

函数调用

我们的函数定义完毕,接下来该是使用它的时候了。函数的使用也有一个术语,叫做:函数调用。

#include 
using namespace std;int addTwo(int number){    int value(number + 2);    return value;}int main(){    int a(2), b(3);        cout << "a的值是 " << a << endl;    cout << "b的值是 " << b << endl;        b = addTwo(a);  // 函数调用        cout << "a的值是 " << a << endl;    cout << "b的值是 " << b << endl;        return 0;}

运行以上程序,显示:

a的值是 2

b的值是 3

a的值是 2

b的值是 4

在调用函数addTwo之后,a的值没有改变;b的值改变了,等于a的值加上2。

详细解释

现在我们来看一个程序,包含一个multipleTwo函数,用于计算一个数的两倍的值。

我们暂时把multipleTwo函数写在main函数之前,如果放在main函数之后会出错,以后的课程我们会解释为什么。

#include 
using namespace std;int multipleTwo(int number){    return 2 * number;}int main(){    int initial = 0, twice = 0;        cout << "请输入一个整数... ";    cin >> initial;    twice = multipleTwo(initial);    cout << "这个数的两倍是 " << twice << endl;        return 0;}

我们的程序是从main函数开始运行的,这个大家已经知道了。

我们首先请求用户输入一个整数,将其值传递给multipleTwo函数,并且把multipleTwo函数的返回值赋给twice这个变量。

仔细看下面这一行,这是我们最关心的一行代码,因为正是这一行调用了我们的multipleTwo函数。

twice = multipleTwo(initial);

在括号里,我们将变量initial当做输入传递给函数,也正是这个变量,函数将其用于内部的处理。

这个函数返回一个值,就是twice这个变量。

其实这一行中,我们就是命令电脑:“让multipleTwo函数给我计算initial的两倍的值,并且将结果储存到twice这个变量中”。

详细的分步解释

也许对于初学者,理解起来还是有些许困难。

不用担心,我相信通过下面的分步解释,大家会明白得更透彻。

这个特殊注释的代码向大家展示了程序的运行顺序:

#include 
using namespace std;int multipleTwo(int number)   // 6{        return 2 * number;    // 7}int main()   // 1{        int initial = 0, twice = 0;   // 2            cout << "请输入一个整数... ";   // 3            cin >> initial;   // 4            twice = multipleTwo(initial);   // 5            cout << "这个数的两倍是 " << twice << endl;  // 8            return 0;  // 9}

上面的编号表示了执行的顺序:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9

  1. 程序从main函数开始执行

  2. 在main函数中的命令一行一行地被执行

  3. 执行cout输出

  4. 执行cin读入数据,赋值给变量initial

  5. 读入指令... 调用multipleTwo函数了,因此程序跳到上面的multipleTwo函数中去执行

  6. 我们运行multipleTwo函数,并接受一个数作为输入(number)

  7. 我们对number做运算,并且结束multipleTwo函数,return意味着函数的结束,并且返回一个值。将返回值赋给twice变量。

  8. 我们重新回到main函数的下一条指令,用cout输出

  9. 又一个return,这次是main函数的结束,于是整个程序运行完毕。

变量initial被传值给multipleTwo的参数number(另一个变量),称为值传递。当然其实原理是做了一份变量initial的拷贝,把拷贝赋值给了number。

这里如果我们把initial改名为number也是可以的,并不会与函数multipleTwo的参数number冲突。因为参数number是属于multipleTwo这个函数的专属变量。

多个参数

上面的addTwo函数只有一个参数,我们也可以定义有多个参数的函数。其实之前见过的pow函数和getline函数就有两个参数。

int addition(int a, int b){    return a+b;}
double multiplication(double a, double b, double c){    return a * b * c;}

以上所定义的两个函数中,我们合理地偷懒了:将参数运算的结果直接作为返回值,而没有定义例如之前在addTwo函数中用到的value这样的变量。这样我们的代码也达到了最简化,我们是鼓励这样做的。程序员要会"偷懒"。

上面的第一个函数addition(英语"加法,相加"的意思)接收两个int型参数,返回它们的相加值。

第二个函数multiplication(英语"乘法,相乘"的意思)接收三个double型参数,返回它们的相乘值。

我们也可以定义参数类型不一样的函数。例如:double different(int a, double b);

无参数

我们的函数也可以没有参数。那么在括号里就不加任何东西。

例如:

string getName(){         cout << "请输入你的名字 : ";     string name;     cin >> name;     return name;}

无返回值的函数

函数也可以没有返回值,那么在定义的时候要将函数返回值类型设为void(英语"虚空,无"的意思)。不用加return语句了。例如:

void sayHello(){        cout << "Hello!" << endl;        // 因为没有返回值,也就没有return语句了}
int main(){    sayHello();    // 因为函数sayHello不返回任何值    // 我们调用的时候也不会将其赋给某个变量了        return 0;}


实例

下面我们会一起看几个函数的实例,以便读者对函数有更深入的了解。我们尽量展示不同情况,使大家看到可能出现的各种函数类型。

欧元/人民币转换

最近欧元兑换人民币的汇率还是很稳定的在7左右徘徊,为什么当年(2009)小编出来法国时,欧元的汇率那么高(10),现在要换人民币却只有7。唉,就是辣么不逢时。

我们就来写一个函数,用于转换欧元到人民币。

查了一下最新的汇率:

1欧元 = 7.0992 人民币元

#include 
using namespace std;double conversion(double euros){ double rmb = 0; rmb = 7.0992 * euros; return rmb;}int main(){    cout << "10 欧元 = " << conversion(10) << "人民币" << endl;    cout << "50 欧元 = " << conversion(50) << "人民币" << endl;    cout << "100 欧元 = " << conversion(100) << "人民币" << endl;    cout << "200 欧元 = " << conversion(200) << "人民币" << endl;    return 0;}

你也可以写一个人民币转换为欧元的小程序。

惩罚

接下来看一个函数,这个函数不会返回任何值,所以类型是void。这个函数会根据传入的参数在屏幕上显示一定次数的信息。这个函数只有一个参数,那就是显示惩罚语句的次数:

#include 
using namespace std;void punish(int lineNumber){ int i; for (i = 0 ; i < lineNumber ; i++) {     cout << "我不应该有钱任性" << endl; }    }int main(int argc, char *argv[]){     punish(5); return 0;}

显示结果如下:

我不应该有钱任性

我不应该有钱任性

我不应该有钱任性

我不应该有钱任性

我不应该有钱任性

矩形面积

矩形的面积很容易计算:长 x 宽。长度乘以宽度。

我们来写一个求矩形面积的函数,它有两个参数:矩形的长和矩形的宽。返回值是矩形的面积:

#include 
using namespace std;double rectangleArea(double length, double width){    return length * width;}int main(){    cout << "长是10,宽是5的矩形面积是 " << rectangleArea(10, 5) << endl;    cout << "长是3.5,宽是2.5的矩形面积是 " << rectangleArea(3.5, 2.5) << endl;    cout << "长是9.7,宽是4.2的矩形面积是 " << rectangleArea(9.7, 4.2) << endl;        return 0;}

显示结果:

长是10,宽是5的矩形面积是 50.000000

长是3.5,宽是2.5的矩形面积是 8.750000

长是9.7,宽是4.2的矩形面积是 40.740000

我们可以直接在函数里显示 长,宽和计算所得的面积吗?

当然可以。这样的情况下,函数就不必返回任何值了,函数计算出矩形面积,然后直接显示在屏幕上:

#include 
using namespace std;void rectangleArea(double length, double width){    double area = 0;    area = length * width;    cout << "长为 "<< length << " 宽为 " << width << " 的矩形的面积是 " << area << endl;}int main(){    rectangleArea(10, 5);    rectangleArea(3.5, 2.5);    rectangleArea(9.7, 4.2);        return 0;}

我们可以看到,cout在函数体内被调用,显示的结果和之前把cout放在main函数里是一样的。只不过我们用的方法不一样罢了。

菜单

我们来写一个餐馆菜单的例子。

#include 
using namespace std;int menu(){    int choice = 0;        while (choice < 1 || choice > 4)    {        cout << "菜单 :" <
<< "1 : 北京烤鸭" <
<< "2 : 麻婆豆腐" <
<< "3 : 鱼香肉丝" <
<< "4 : 剁椒鱼头" <
<< "您的选择是 ? " <
> choice;    }        return choice;}int main(){    switch (menu())    {        case 1:            cout << "您点了北京烤鸭" << endl;            break;        case 2:            cout << "您点了麻婆豆腐" << endl;            break;        case 3:            cout << "您点了鱼香肉丝" << endl;            break;        case 4:            cout << "您点了剁椒鱼头" << endl;            break;    }        return 0;}

这个程序还可以改进:你可以在用户输入一个错误的数字时显示一个错误信息,而不是直接继续让其点单。


总结

  1. 函数包含具有特定目的的一段代码。

  2. 每个C++程序至少有一个函数:main函数。这是程序的入口。

  3. 把程序切分为有特定作用的各个函数是管理代码的好方式。

  4. 程序中,我们可以多次调用同一个函数。

  5. 一个函数可以接收数据(通过参数),也可以返回数据(通过return)。


第一部分第八课预告

今天的课就到这里,一起加油吧!

下一课我们学习:传值引用,文件源头