03. 函数
03. 函数
模块化编程—函数
(1)模块化编程的含义
将重复性的代码封装成一个方法,需要的时候调用这个方法。
一般将这样的方法称为函数,完成方法的过程就叫模块化编程。
(2)函数一般划分为自定义函数和系统函数
自定义函数:用户自己定义函数的功能
系统函数:系统函数就是系统自带的函数,它的使用需要引入头文件。
【例】给出平面上两个点的坐标,求两点之间的曼哈顿距离。
平面上A点 (x1, y1) 与B点 (x2, y2) 的曼哈顿距离为:
dis = |x1-x2|+|y1-y2|
(1)使用#include
(2)自己实现一个abs()函数
- 使用库函数abs()
#include <iostream>
#include<cmath> // abs()
using namespace std;
int main() {
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
cout<<abs(x1-x2)+abs(y1-y2);
return 0;
}
- 自定义函数abs()
#include <iostream>
using namespace std;
int abs(int x) {
if(x>0) return x;
return -x;
}
int main() {
int x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
cout<<abs(x1-x2)+abs(y1-y2);
return 0;
}
自定义函数的优点
(1)程序结构清晰,逻辑关系明确,程序可读性强;
(2)解决相同或相似问题时不需要重复编写代码,减少代码量;
(3)利用函数实现模块化编程,各模块功能相对独立,利用“各个击破“,降低调试难度
函数的定义格式
返回类型 函数名(参数列表){
函数体
}
double abs(double x){
if(x>0) return x;
return -x;
}
无返回类型 函数名(参数列表){
函数体
}
void display(char n){
cout<<n<<endl;
}
- 函数名是标识符,按照变量名的规则命名。
- 参数列表可以为空,即无参函数,也可以有多个参数,参数之间用逗号隔开,参数列表中的每个参数,由参数类型说明和参数名组成。
- 函数体是实现函数功能的语句,返回类型是void的函数无返回值,其余函数执行过程中碰到return语句,将在执行完return语句后直接退出函数,不去执行后面的语句。
- 返回值的类型一般是int,double,char等类型,也可以是数组。有时候函数不需要返回任何值,这时只需要定义函数返回类型为void。
根据上述定义,我们知道C++函数形态有以下四类:
- 返回类型 函数名(参数列表)
- 返回类型 函数名()
- void 函数名(参数列表)
- void 函数名()
【例】用函数解决如下问题:
输出长宽高分别为 3cm,4cm,5cm的立方体的表面积,体积。
- 参考程序
double area(int a,int b,int c){ //表面积
return 2*(a*b+a*b+b*c);
}
double vol(int a,int b,int c){ //体积
return a*b*c;
}
【例】编写函数输出斐波那契数列的第n项(n≤20)。
其中斐波那契数列f(n)的定义如下:f(1)=0, f(2)=1, f(n)=f(n-1)+f(n-2)。
#include <iostream>
using namespace std;
int fib(int n){
int f[20]={0,1};
for(int i=2; i<n; i++){
f[i]=f[i-1]+f[i-2];
}
return f[n-1];
}
int main() {
int n; cin>>n;
cout<<fib(n);
return 0;
}
声明方法如下:返回类型 函数名(参数列表);
//只定义不声明
int sgn(int n){ //定义实现sgn函数
...
}
int main(){
cout<<sgn(3); //调用sgn函数
}
----------------------------------
//先声明,再实现
int sgn(int n); //声明sgn函数
int main(){
cout<<sgn(3); //调用sgn函数
}
int sgn(int n){ //实现sgn函数
...
}
函数的调用
-
调用方法
根据返回类型可以将函数分为两大类:有返回值,没有返回值。
(1)对于有返回值的函数,调用时必须以值的形式出现在表达式中,比如:
int mht=abs(x1-x2)+abs(y1-y2);
(2)对于没有返回值的函数,直接写:函数名(参数);
调用发生在定义之后,直接调用即可;
调用发生在定义之前,则要在调用之前声明,才可以调用。 -
形式参数和实际参数
(1)函数定义中的参数名称为形式参数,形式参数变量名可以替换。
如 long long C(int n, int m)中的 n与 m是形式参数
(2)实际参数是指实际调用函数时传递给函数的参数的值。
如调用函数 C(6,3),这里 6, 3就是实际参数,其中 6传递给 n,3传递给m。 -
调用函数的执行过程
(1)计算实际参数的值
(2)把实际参数传递给被调用函数的形式参数,程序执行跳到被调用的函数中。
(3)执行函数体,执行完后如果有返回值,则把返回值返回给调用该函数的地方继续执行。
函数的参数传递
-
传递参数
函数通过参数来传递输入数据,参数通过传值机制来实现。
前面的程序中的函数都采用了传值参数,采用的传递方式是值传递,
函数在被调用时,用克隆实参的办法得到实参的副本传递给形参,
改变函数形参的值并不会影响外部实参的值。 -
引用参数
函数定义时在变量类型符号之后形式参数名之前加 "&",则该参数是引用参数,
把参数声明成引用参数,实际上改变了缺省的按值传递参数的传递机制,
引用参数会直接关联到其所绑定的对象,而并非这些对象的副本,
形参就是对应形参的别名,形参的变化会保留到实参中
【例】写一个swap函数,交换两个变量的值。
- 错误程序
#include <iostream>
using namespace std;
void swap(int a, int b){
int temp=a; a=b; b=temp;
}
int main() {
int a, b; cin>>a>>b;
swap(a, b);//这样可以实现交换么? NO
cout<<a<<" "<<b<<endl;
return 0;
}
- 参考程序
#include <iostream>
using namespace std;
//通过引用&,建立别名,指向实际参数
void swap(int& a,int& b){
int c=a; a=b; b=c;
}
int main() {
int a,b; cin>>a>>b;
swap(a,b);
cout<<a<<" "<<b;
return 0;
}
变量的作用域
程序中所用到的变量并不总是有效可用的,限定可用范围就是它的作用域。
由于作用域的不同,我们把变量分为两种:全局变量和局部变量。
#include<iostream>
using namespace std;
int a,b; //全局变量
int main(){
int c,d; //局部变量
return 0;
}
-
全局变量
定义在函数外部的变量称为全局变量。
全局变量的作用域是从变量定义的位置开始到文件结束。
(1)变量x,y定义在所有的花括号外部,具有全局作用域,是全局变量
(2)全局变量的作用域使得函数间多了一种传递信息的方式
(3)过多的使用全局变量,会增加调试的难度,会降低程序的通用性
(4)全局变量在函数执行的全过程中一直占用内存单元
(5)全局变量在定义时若没有赋初值,则为0 -
局部变量
定义在函数内部作用域为局部的变量称为局部变量。
函数的形参和在该函数内定义的变量都称为该函数的局部变量。
(1)局部变量只在块内可见,块外无法访问
(2)不同函数的局部变量相互独立,不能访问其他函数的局部变量
(3)局部变量的存储空间是临时分配的,函数执行完毕,该空间就被释放
(4)定义在全局作用域中的变量可以在局部作用域中使用,
变量还可以在内部作用域中被重新定义,
定义在内部作用域的名字将会自动屏蔽定义在外部作用域的相同的名字。
案例练习
【例】输入两个正整数,编程计算两个数的最大公约数。
- 最小递减法
先找a,b的最小值,判断该值能否同时被a,b整除,
如果可以,该数就是答案,
否则每次-1,继续判断,直到找到答案。
int gcd(int a, int b)
- 参考程序
#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int a, int b){// 最小递减法
for(int i=min(a,b); i>=1; i--){
if(a%i==0 && b%i==0) return i;
}
}
int main(){
int a,b; cin>>a>>b;
cout<<gcd(a,b)<<endl;
return 0;
}
- 更相减损法
a-b=c,则 a,b 的最大公约数就是 b,c 的最大公约数,
如果 c=0,a 就是答案。
int gcd(int a, int b)
- 参考程序
#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int a, int b){// 更相减损法
if(a<b) swap(a, b);
while(a-b){
if(a<b) swap(a,b);
int c=a-b;
a=b, b=c;
}
return a;
}
int main(){
int a,b; cin>>a>>b;
cout<<gcd(a,b)<<endl;
return 0;
}
- 辗转相除法
a/b=q...r,则 a,b 的最大公约数就是 b,r 的最大公约数,
如果 r=0, 则 b 就是答案。
int gcd(int a, int b)
- 参考程序
#include<iostream>
#include<algorithm>
using namespace std;
int gcd(int a, int b){// 辗转相除法
int r=a%b;
while(r){
r = a%b, a = b, b = r;
}
return a;
}
int main(){
int a,b; cin>>a>>b;
cout<<gcd(a,b)<<endl;
return 0;
}
【例】输入两个正整数,编程计算两个数的最小公倍数。
- 最大递增法
先找 a,b 的最大值,判断该值能否同时整除 a,b,
如果可以该数就是答案,否则每次+1,继续判断,直到找到答案。
int lcm(int a, int b)
- 参考程序
#include<iostream>
#include<algorithm>
using namespace std;
int lcm(int a, int b){// 最大递增法
for(int i=max(a,b); ; i++){
if(i%a==0 &&i%b==0) return i;
}
}
int main(){
int a,b; cin>>a>>b;
cout<<lcm(a,b)<<endl;
return 0;
}
- 定理法
两个数的乘积等于这两个数的最大公约数与最小公倍数的乘积。
int lcm(int a, int b){
return a*b/__gcd(a,b);
}
总结:
求最大公约数的三种方法:
-
最小递减法:先找a,b的最小值,判断该值能否同时被a,b整除,
如果可以该数就是答案,否则每次-1,继续判断,直到找到答案。 -
更相减损法
a-b=c,则a,b的最大公约数就是b,c的最大公约数,如果c=0,a就是答案。 -
辗转相除法
a/b=q...r,则a,b的最大公约数就是b,r的最大公约数,如果r=0,则b就是答案。
求最小公倍数的两种方法:
-
最大递增法:先找a,b的最大值,判断该值能否同时整除a,b,
如果可以该数就是答案,否则每次+1,继续判断,直到找到答案。 -
定理法:两个数的乘积等于这两个数的最大公约数与最小公倍数的乘积。
课后练习推荐题目: