一、函数是什么
我们从函数的产生来看,其实很好理解,因为某些代码或运算需要反复使用,作为一名志向远大(懒癌后期)的OIer,我们往往不想反反复复地写同样的代码(有些可能要改一些数据),所以我们想要有一个“东西”来专门实现我们的运算,我们期待只需要写一遍代码,以后要使用的时候,直接调出来就可以,这个“东西”,在C++和很多语言中,都叫做函数。因为很多时候需要改动一些数据,我们在调用的时候也往往要带上这写改动的数据,这些数据就是我们函数中的参数了,当然也有一些函数根本就不用改数据,也可以不带参数。
举个例子说明一下:
我们要求:1!+2!+3!+4!+5!+6!+...+20!=?
很显然,用前面的知识,我们可以轻易地用两重循环来实现求和。
int sum=0;
for(int i=1; i<=20; i++){
int jc=1;
for(int j=1; j<=i; j++)
jc*=i;
sum+=jc;
}
// sum即是结果
但有时,我们又会觉得它并不是特别方便,因为有时我们求阶乘的时候,不一定是像现在这么规律,从1到n的阶乘,譬如:
求:3!+10!+13!=?
我们总不希望把阶乘这样一个过程重复地写几遍吧。这时候,我们不妨换一个思路来看,在前面的经验中,我们已经使用过cmath库中的sqrt(),其实这就是一个数学库的函数,既然有开平方的函数,那有没有一个阶乘的函数呢?我们暂且假设有阶乘的函数,并且设它为jc(),那么我们可以这样来实现。
#include <iostream>
#include <cmath>
using namespace std;
int main(){
int sum=0;
sum+=jc(3);
sum+=jc(10);
sum+=jc(13);
cout<<sum<<endl;
return 0;
}
但是遗憾的是,在数学库中,并没有这样一个函数。一般库函数中都是包含了最基础、最常用的函数,在数学库的设计者眼中和实践中,阶乘并不算特别常用的函数,因此并没有收录到数学库中。但是也没有关系,我们完全可以自己定义一个函数,譬如就定义为jc(),那么我们的程序就可以这样写了。
#include <iostream>
using namespace std;
// 注意:这是jc的函数
int jc(int x){
int t=1;
for(int i=1; i<=x; i++)
t*=i;
return t;//返回结果
} //函数结束
int main(){
int sum=0;
sum+=jc(3);
sum+=jc(10);
sum+=jc(13);
cout<<sum<<endl;
return 0;
}
总结一下,其实函数很简单,就是我们不希望重复写代码,所以想借助函数来减少重复代码,通过函数来实现某个动能。同时我们留意到,函数实现某些功能或计算,很多时候会带入一些数据,也就是参数,很多时候还需要得到一个结果,也就是返回值的问题。
二、函数在程序中的结构和调用关系
函数定义,参照书本知识,举个例子说明函数的结构和使用方法。(要求实现最简单的加法功能)
#include <iostream>
using namespace std;
int main(){
int a, b;
cin>>a>>b;
int c=add(a, b);
cout<<c;
reuturn 0;
}
我们在写简单程序时(区别于工程型编程),往往会先写函数的主体结构和框架,然后在去填充具体的功能,表现在编程中,其实就是先写主程序main,然后要实现什么功能,先假设有一个函数,可以实现那个功能,一路写下来。然后再去完善具体的功能,最后需要测试数据和考虑各种可能的bug。
函数写法,有两种,一种是写在主程序前,不要事先声明;一种是写在主程序后,但是要在主程序前声明函数。在这里,基于OI或ACM类型的比赛,建议大家把函数都写在main前面,并且越先使用的函数写在最上面,可以减少没必要的函数声明和定义。像上面的一个案例,假如我们要实现一个加法的功能,我们可以这样写。
在写的时候,我们就要考虑几个问题:
1、函数需要返回值吗?什么类型?
2、需要参数吗?几个参数?分别什么类型?
3、需要改变本身传递的数据吗?即是考虑“传值”还是“传址”,还有全局变量与局部变量的考虑。
4、测试bug,特殊值
参照上面几个问题,我们逐个考虑:要得出相加的结果,显然需要返回值,而且应该是int类型;参数的话,因为是两个数相加,因此两个加数就是参数,类型也是int;我们只是需要得到一个计算的结果,并需要去改变两个加数的原始数字,因此考虑"传值",不涉及全局数据,局部变量即可;特殊值考虑负数、零等的相加。
完整代码如下:
#include <iostream>
using namespace std;
int add(int x, int y){
return (x+y); //建议加上括号
}
int main(){
int a,b;
cin>>a>>b;
int c=add(a, b);
cout<<c;
return 0;
}
需要说明的是,main()本身就是函数,并且是主函数,所有程序都必须用main开始,并且遵循顺序结构的规则。那么涉及到函数的各种调用的时候,我们需要稍微加以理解。从main开始,从上往下,从左往右,执行,遇到函数f1,就跳转到函数f1处执行,直到遇到f1中的return语句或f1函数结束,才重新回到之前调用的地方。打个比方:我们来到学校上课,都是按照一定顺序进行的,每次都是先过周一、周二、周三...一直到放学。那么函数的引入,就像是我们在周二晚上,我们做了一个梦,梦见我们要去做一些事情,做完梦之后,我们又重新回到做梦的那个时间点,也就是回到周二,再过周三,一直下去。有点意思的是,梦里可能还会再有梦,实际上,这就是在函数中再调用函数的意思,如果梦里继续做了同样的梦,那就是所谓的递归了。
基础练习
1、求两个正整数的最大公约数(辗转相除法)
2、进制的转换(a.由十进制转二进制,b.由二进制转十进制,c.m进制转n进制)
3、更多练习,参见函数过关题和NOI官网题库。