1、课程大纲
2、第1部份第9课:函数
3、第1部份第10课预告: 练习题+习作
我们的课程分为4大部份,每个部份结束后都会有练习题,并会公布答案。还会带大家用C语言编写3个游戏。
C语言编程基础知识
甚么是编程?
工欲善其事,必先利其器
你的第1个程序
变量的世界
运算那点事
条件表达式
循环语句
实战:第1个C语言小游戏
函数
练习题
习作:完善第1个C语言小游戏
模块化编程
进击的指针,C语言王牌
数组
字符串
预处理
创建你自己的变量类型
文件读写
动态分配
实战:“悬挂小人”游戏
安全的文本输入
练习题
习作:用自己的语言解释指针
安装SDL
创建窗口和画布
显示图象
事件处理
实战:“超级玛丽推箱子”游戏
掌握时间的使用
用SDL_ttf编辑文字
用FMOD控制声音
实战:可视化的声音谱线
练习题
链表
堆,栈和队列
哈希表
练习题
最近各种社交媒体和新闻都被姚贝娜和杰伦刷屏了。1个使人惋惜,1个使人高兴。
我觉得怎样也得让编程来刷1下屏吧。哈哈,开玩笑的,今天就用函数来刷屏好了。
说是刷屏,其实也是翻页,由于这1课我们将会用函数这个重中之重来结束《C语言探索之旅》的第1部份(基础部份),而第2部份将要迎接我们的就是C语言的高级技术了。刷走了1个部份,准备迎接新的挑战。
第2部份会比较难哦,不过不用担心,我们1点点学习。只要方向对,肯花时间,C语言其实不可怕。
这1课里我们也会给大家讲C语言程序所基于的原则。
我们将要学习如何将程序分块管理,有点像乐高积木...
其实所有C语言的大型程序都是小程序块的集合,而这些小程序块我们称之为函数。在面向对象的语言如Java里面,函数又被称为方法,固然这里我们只讨论C语言(面向进程的语言),不讨论面向对象的语言,sorry,我又空话了... 好吧,我们只面向C语言这个对象。
函数的创建和调用
在之前的课程中我们已学过所有的C语言程序都是由main函数开始运行的。那时候我们也展现了1个概要图,里面有1些术语:
最上面的部份我们称之为“预处理指令”,很容易辨识,由于以#号开头,而且总是放在程序的最前面。
下面的部份就是我们要学习的函数了,这里的示例是main函数。
前面说过,C语言的程度都是以main函数为入口函数的。1个C程序要运行,必须要有main函数。只不过,目前为止我们写的所有程序 包括上1课的小游戏,也只是在main函数里面捣鼓而已,我们还没跳出过main函数过。没办法,有钱任性,没钱只能…
那你要问了:这样不好吗?
答案是:其实不是说这样不好,但这其实不是C程序员平时所做的。几近没有程序员会只在main函数的大括号内部写代码。到目前为止我们所写的程序都还比较短小,但是想象1下如果程序变得很大,代码几千几万乃至上百万行,难道我们还把这些代码都塞在main函数里面吗?
所以我们现在来学习如何更好地计划我们的程序。我们要学习将程序分成很多小块,就像乐高积木的每个小块1样,这些小块搭起来却可以组成很多好玩的形状。这些程序小块我们称其为函数。
1个函数会履行某些操作,并返回1个值。程序就是1个代码序列,负责完成特定的任务。
我们说1个函数有输入和输出,以下图所示:
可以把函数想象成1台制作香肠的机器,在输入那1头你把猪装进去,输出那1头就出来香肠了。这酸爽。
当我们在程序中调用1个函数的时候,会顺次产生3个步骤:
输入:给函数传入1些信息(借着给函数1些参数)
运算:因着输入里传进去的信息,函数就能够完成特定任务了
输出:做完运算后,函数会返回1个结果。我们称其为输出或返回值
举个实际例子,比如我们有个函数叫做multipleTwo,作用是将输入乘以2,以下所示:
函数的目的是为了让源代码更加结构分明,也节省源代码数目,由于我们就不用每次都输入重复的代码片断而只需要调用函数就行了。
再假想1下:以后我们可能会想要创建1个叫showWindow(显示窗口)的函数,作用是在屏幕上显示1个窗口。1旦函数写好以后(固然写的进程是最难的),我们就只需要说:“那个谁,给我去打开1个窗口”,showWindow函数就会为我们在屏幕上显示1个窗口。我们也能够写1个displayPersonage的函数,作用是为我们在屏幕上显示1个游戏人物。
函数的构成
我们在之前的课中已接触过函数了,就是非常重要的main函数。但是还是需要我们来介绍1下1个函数的构成究竟是怎样样的。
下面是函数的语义学的结构,这是1个需要了解的模板:
类型 函数名(参数)
{
// 函数体,在这里插入指令
}
关于这个模板我们需要掌握4点:
函数类型:对应输出类型,也能够把其看作函数的类型。和变量类似,函数也有类型,这类型取决于函数返回值的类型。如果1个函数返回1个浮点数(带小数点的),那末自然我们会将函数类型定为float或double;如果返回整数,那末我们1般会将类型定为int或long。但是我们也能够创建不返回任何值的函数。
函数名:这是你的函数的名字。你可以给你的函数起任意名字,只要遵从给变量命名的相同的规则就好。
函数的参数(对应输入):参数位于函数名以后的圆括号内。这些参数是函数要用来做操作(运算)的数据。你可以给函数传入任意数量的参数,也能够不传入任何参数。
函数体:大括号规定了函数的起始和结束范围。在大括号中你可以写入任意多的指令。对上面的multipleTwo函数,需要写入将输入的数字乘以2的相干操作指令。
根据函数类型,函数可以分为两类:
返回1个值的函数,这样的函数,我们将其类型定为对应的值的类型(char,int,long,double等)
不返回任何值的函数,这样的函数,我们将其类型定为void(void在英语中是“空的,无效的”之意)
创建函数
我们不要再迟延了,马上给出1个实例。用的还是我们上面提过的multipleTwo这个函数:这个函数的输入是1个整型int,输出也是int类型的数。
int multipleTwo(int number)
{
int result = 0;
result = 2 * number; // 我们将提供的数乘以2
return result; // 我们将2倍的数返回
}
这就是你的第1个除main之外的函数,自豪不?
return result;
这句话1般放在函数体的最后,用于返回1个值。这句话意味着:“函数你给我停下,然后返回这个值”。这里的result必须是int类型的,由于函数类型是int,所以返回值也必须是int类型。
result这个变量是在multipleTwo函数中声明/创建的,所以它只能在这个函数里面用,不能在另外一个函数比如main中使用,所以是multipleTwo函数的私有变量。
但上面的代码是否是最简单的呢?
不是,还可以简化,以下:
int multipleTwo(int number)
{
return 2 * number;
}
上面的代码做的是1样的事情,写起来也更简单,函数体内只有1句话。
通常来讲,我们写的函数都会有多个变量,以便做运算,multipleTwo这个函数算是相当简单了。
多个参数,或没有参数
多个参数
我们的multipleTwo函数只有1个参数,但是我们也能够创建有几个参数的函数,比以下面这个加法函数addition:
int addition(int a, int b)
{
return a + b;
}
可以看到,只需要用1个逗号来分隔参数就行了。
没有参数
有些函数,虽然不太常见,可能会没有参数。例如1个用来显示Hello(你好)的函数:
void hello()
{
printf("Hello");
}
如上所见,这个函数没有任何参数,另外,可以看到我们还把函数类型定为了void,就是空,所以也没有return语句用于返回1个值,所以这个函数也没有返回值。彻彻底底的黑5类…
调用函数
现在我们来看1个程序,温习1下我们之前学的内容。
我们要用到我们的multipleTwo函数,来计算1个数的两倍的值。
我们暂时把multipleTwo函数写在main函数之前,如果放在main函数以后会出错,以后的课程我们会解释为何。
#include <stdio.h>
int multipleTwo(int number)
{
return 2 * number;
}
int main(int argc, char *argv[])
{
int initial = 0, twice = 0;
printf("请输入1个整数... ");
scanf("%d", &initial);
twice = multipleTwo(initial);
printf("这个数的两倍是 %d ", twice);
return 0;
}
我们的程序是从main函数开始运行的,这个大家已知道了。
我们首先要求用户输入1个整数,将其值传递给multipleTwo函数,并且把multipleTwo函数的返回值赋给twice这个变量。
仔细看下面这1行,这是我们最关心的1行代码,由于正是这1行调用了我们的multipleTwo函数。
twice = multipleTwo(initial);
在括号里,我们将变量initial当作输入传递给函数,也正是这个变量,函数将要用于其内部的处理。
这个函数返回1个值,这个值我们赋给twice这个变量。
其实这1行中,我们就是命令电脑:“让multipleTwo函数给我计算initial的两倍的值,并且将结果贮存到twice这个变量中”。
详细的分步解释
或许对初学者,理解起来还是有些许困难。
不用担心,我相信通过下面的分步解释,大家会明白得更透彻。
这个特殊注释的代码向大家展现了程序的运行顺序:
#include <stdio.h>
int multipleTwo(int number) // 6
{
return 2 * number; // 7
}
int main(int argc, char *argv[]) // 1
{
int initial = 0, twice = 0; // 2
printf("请输入1个整数... "); // 3
scanf("%d", &initial); // 4
twice = multipleTwo(initial); // 5
printf("这个数的两倍是 %d ", twice); // 8
return 0; // 9
}
上面的编号表示了履行的顺序:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
程序从main函数开始履行
在main函数中的命令1行1行地被履行
履行printf输出
履行scanf读入数据,赋值给变量initial
读入指令... 啊,调用multipleTwo函数了,因此程序跳到上面的multipleTwo函数中去履行
我们运行multipleTwo函数,并接受1个数作为输入(number)
我们对number做运算,并且结束multipleTwo函数,return意味着函数的结束,并且返回1个值
我们重新回到main函数的下1条指令,用printf输出
又1个return,这次是main函数的结束,因而全部程序运行终了。
变量initial被传值给multipleTwo的参数number(另外一个变量),称为值传递。固然其实原理是做了1份变量initial的拷贝,把拷贝赋值给了number,这个值传递的概念以后学习指针那1章会再详述。
这里如果我们把initial改名为number也是可以的,其实不会与函数multipleTwo的参数number冲突。由于参数number是属于multipleTwo这个函数的专属变量。
测试程序
下面是程序运行起来的1个实例:
请输入1个整数... 10
这个数的两倍是 20
固然你没必要将multipleTwo函数的返回值赋给1个变量,也能够直接将multipleTwo函数的返回值传递给另外一个函数,就好像multipleTwo(intial)是1个变量1般。
仔细看下面这个程序,跟上面几近是1样的,但是修改了最后1个printf的行动,我们也没有使用twice这个变量,由于没必要要:
#include <stdio.h>
int multipleTwo(int number)
{
return 2 * number;
}
int main(int argc, char *argv[])
{
int initial = 0, twice = 0;
printf("请输入1个整数... ");
scanf("%d", &initial);
// 函数的结果(返回值)直接传递给printf函数,而没有通过第3方变量
printf("这个数的两倍是 %d ", multipleTwo(initial));
return 0;
}
我们可以看到,这次的程序直接将multipleTwo函数的返回值传递给了printf函数。
当程序运行到这1行会产生甚么呢?
很简单,电脑看到这1行是printf函数,所以调用标准输入输出库的printf函数,向printf函数传递我们给的所有参数。第1个参数是要显示的语句,第2个参数是1个整数。电脑又知道要把这个整数值传递给printf函数,必须先调用multipleTwo函数,所以它就乖乖地去调用multipleTwo函数,做两倍乘法运算,并且直接把结果传递给printf函数。
这就是函数的层叠式调用,这样做的好处是,1个函数可以按需调用另外一个函数。
只要愿意,我们的multipleTwo函数也能够再调用其他的函数,只要你肯写,然后这个函数再调用其它函数,顺次类推。
这就是C语言程序所基于的原则。所有的代码都是有计划地组合在1起的,类似乐高积木。
最后,最艰巨确当然是编写函数了,1旦完成,你就只需要调用它就行了,不需要太担心函数内部所做的运算。使用函数可以大大下降代码的重复度,相信我,你会非常需要函数的。
1些函数的实例
如果1起学习过之前的课程,你应当会有这类印象:我就是个例子狂人。
是的,由于我很喜欢用实例来加深理解。我可不是甚么superhero(超级英雄),比如Iron Man,绿伟人,蚁人,等。
由于我觉得理论虽好,但如果只有理论,那我们就不能很好地掌握知识,而且不知道怎样利用,那就很惋惜了。想起了“劲酒虽好,可不要贪杯哦”那句广告词…
所以下面我们会1起看几个函数的实例,以便读者对函数有更深入的了解。我们尽可能展现不同情况,使大家看到可能出现的各种函数类型。
欧元/人民币转换
最近欧元大贬值,已降到7左右了,我们就来写1个函数,用于转换欧元到人民币。
查了1下最新的汇率:
1欧元=7.1874人民币元
#include <stdio.h>
double conversion(double euros)
{
double rmb = 0;
rmb = 7.1874 * euros;
return rmb;
}
int main(int argc, char *argv[])
{
printf("10 欧元 = %f 人民币 ", conversion(10));
printf("50 欧元 = %f 人民币 ", conversion(50));
printf("100 欧元 = %f 人民币 ", conversion(100));
printf("200 欧元 = %f 人民币 ", conversion(200));
return 0;
}
你也能够写1个人民币转换为欧元的小程序。
惩罚
接下来看1个函数,这个函数不会返回任何值,所以类型是void。这个函数会根据传入的参数在屏幕上显示1定次数的信息。这个函数只有1个参数,那就是显示惩罚语句的次数:
#include <stdio.h>
void punish(int lineNumber)
{
int i;
for (i = 0 ; i < lineNumber ; i++)
{
printf("我不应当有钱任性 ");
}
}
int main(int argc, char *argv[])
{
punish(5);
return 0;
}
显示结果以下:
我不应当有钱任性
我不应当有钱任性
我不应当有钱任性
我不应当有钱任性
我不应当有钱任性
矩形面积
矩形的面积很容易计算:长 x 宽。
我们来写1个求矩形面积的函数,它有两个参数:矩形的长和矩形的宽。返回值是矩形的面积:
#include <stdio.h>
double rectangleArea(double length, double width)
{
return length * width;
}
int main(int argc, char *argv[])
{
printf("长是10,宽是5的矩形面积是 %f ", rectangleArea(10, 5));
printf("长是3.5,宽是2.5的矩形面积是 %f ", rectangleArea(3.5, 2.5));
printf("长是9.7,宽是4.2的矩形面积是 %f ", rectangleArea(9.7, 4.2));
return 0;
}
显示结果:
长是10,宽是5的矩形面积是 50.000000
长是3.5,宽是2.5的矩形面积是 8.750000
长是9.7,宽是4.2的矩形面积是 40.740000
我们可以直接在函数里显示 长,宽和计算所得的面积吗?
固然可以。这样的情况下,函数就没必要返回任何值了,函数计算出矩形面积,然后直接显示在屏幕上:
#include <stdio.h>
void rectangleArea(double length, double width)
{
double area = 0;
area = length * width;
printf("长为 %f 宽为 %f 的矩形的面积是 %f ", length, width, area);
}
int main(int argc, char *argv[])
{
rectangleArea(10, 5);
rectangleArea(3.5, 2.5);
rectangleArea(9.7, 4.2);
return 0;
}
我们可以看到,printf函数在函数体内被调用,显示的结果和之前把printf放在main函数里是1样的。只不过我们用的方法不1样罢了。
菜单
还记得之前的课程中菜单的那个例子吗?(皇上,您还记得大明湖畔的夏雨荷么?)
这次我们用自定义的函数来重写1次,会更详细和优化:
#include <stdio.h>
int menu()
{
int choice = 0;
while (choice < 1 || choice > 4)
{
printf("菜单 : ");