c++中的函数

我们把函数看做是一个盒子,它会有以下几个特性:

    1. 开始执行前,函数可以输入一些值
    2. 执行过程中,函数可以做一些事情
    3. 结束执行后,函数可以返回一些值

函数的写法公式:

函数返回值类型  函数名(参数1,参数2,参数n){
    函数体;
    return 函数返回值;
}

花括号包括的被称为函数体 ,注意函数体一定要被花括号包括且不可省略。花括号上面的函数名、函数参数及返回值被称作函数头 。

例如:我们想要计算两个变量ab相加的结果,可以将其写作一个函数:

    1. 输入一些值:输入a,b
    2. 做一些事情:计算a+b
    3. 返回一些值:返回ab的和

我们将这个函数命名为add,代码如下:

int add(int a, int b){
 	return a+b;   
}

注意:每个输入参数必须指明其变量类型,不能省略变量类型。

int add(int a, int b)   // 正确
int add(int a, b)       // 错误

我们把函数名取名为add。当然自定义函数的函数名可以按照自己的喜好来写,就算写成aaaaa也行。 不过,为了函数名拥有语义化,方便人阅读理解,我们一般使用纯英文来作为函数名。

2. 函数的调用

这一段代码被称之为add函数的定义 ,直接运行这段代码你会发现程序并没有任何结果,因为函数需要被调用才能执行。

int add(int a, int b){
 	return a+b;   
}

函数调用的公式:

函数名(参数1,参数2,参数n);

例如:我们用 main函数来调用 add函数。 在这个程序中, main被称作主调函数,add被称作被调函数。

#include <iostream>
using namespace std;
int add(int a, int b){
 	return a+b;   
}
int main(){
    int c = add(1,2); // 函数调用
    cout<<c;
    return 0;
}

main中,将 12 两个参数传入了add函数,并调用add函数。

add函数头中,标明了函数的返回值类型为int,说明这个函数被调用后将返回一个int类型的结果。所以,我们使用int类型的变量c存放add函数的返回值。

函数调用的注意事项:

    • 对函数的调用,也是一个表达式
    • 函数调用表达式的值,由函数内部return语句决定

3. 函数的返回值

函数向函数体外传递数据信息,因此函数结束的位置必须用return语句返回正确的值。

return语句的格式为:

return 表达式;

返回值的类型的类型有:voidintdoublefloatcharbool

其中void表示空类型,即没有返回值。 你也可以在函数参数的括号中填上void,明确表示函数不需要参数。

例如:

#include<iostream>
using namespace std;

void start(void){  //无参数且无返回值的函数
    cout<<"********"<<endl;
    return;     //无返回值,可以省略
}

int main() {
    start();   //调用无参数且无返回值的函数
    return 0;
}

输出结果:

对于没有返回值的函数,可以省略return。 函数运行完花括号内的语句后,就自动结束。

return的作用是返回表达式的值, 若函数需要返回值,则必须使用return带回一个返回值才能正常通过编译。

最后,不要忘记了,return可以出现在函数的任意位置。一旦程序执行到return,就会停止函数的执行, 返回主调函数。

#include<iostream>
using namespace std;

void start(void){  
    cout<<"****"<<endl;
    cout<<"******"<<endl;
    cout<<"********"<<endl;
    return; //函数体内return以下语句不再执行   
    cout<<"**"<<endl; 
    cout<<"*"<<endl;
}

int main() {
    start();   
    return 0;
}

输出结果:

4. 函数的声明

思考:如果函数A内部调用了B,B内部调用了A,哪个写前面?

#include<iostream>
using namespace std;

void functionA(){   //函数A中调用了函数B
	functionB();
	return;
} 
void functionB(){  //函数B中调用了函数A
	functionA();
	return;
} 

int main() {
    functionA();
    functionB();
    return 0;
}

运行结果:

一般来说函数的定义必须出现在函数调用语句之前,否则调用语句编译出错。 在函数functionA()调用前,编译器没有读到任何有关于functionB()的说明 。 编译器不知道functionB()代表什么,它究竟是变量还是函数或者是其他实体。这让编译器十分困惑,因此无法成功编译。

在一个源文件中,如果函数调用前没有函数定义,那么可以使用函数声明通知编译器,告诉编译器有这个函数存在。

函数声明的写法非常简单:函数头 + 分号

例如:

#include<iostream>
using namespace std;

void functionB();  //函数声明

void functionA(){   //函数A中调用了函数B
	functionB();
	return;
} 
void functionB(){  //函数B中调用了函数A
	functionA();
	return;
} 

int main() {
    functionA();
    functionB();
    return 0;
}

Tips: 函数声明也被称作函数原型。

总结

  • 函数三要素:输入一些值、做一些事情、返回一些值
  • 函数的调用公式:函数名(参数1,参数2,参数n);
  • 函数返回值格式:return 表达式;
  • 函数声明的格式:函数头 + 分号

第二节 函数的参数

1. 形参与实参

让我们再回到add函数,add函数的两个参数分别为 int类型,返回值也为 int类型。

#include <iostream>
using namespace std;
int add(int a, int b){
 	return a+b;   
}
int main(){
    int c = add(1,2); // 函数调用
    cout<<c;
    return 0;
}

add函数参数列表中的ab被称作形式参数,简称形参。它指代函数参数的类型,以及参数进入add后,需要经历的处理步骤,没有确定值。

而在函数调用中 add(1, 2)23被称作实际参数,简称实参,它们将确定形式参数的值具体是什么。

2. 参数的自动转换

一般而言,形式参数与实际参数的类型是一致的,但是也允许不一致的情况。

我们将实际参数 1.12.2 传递给形式参数int a, int b ,编译运行一下看看会发生什么。

#include <iostream>
using namespace std;
int add(int a, int b){
 	return a+b;   
}
int main(){
    int c = add(1.1,2.2); 
    cout<<c;
    return 0;
}

输出结果:

从编译结果可以得出,将实际参数 1.12.2传递给形式参数int a,int b时,编译器会尝试将实参转换为形参的类型。

若可以转换,那么将编译通过。转换过程中可能出现数据丢失 ,例如:1.12.2 被转换为了整型 12,小数部分被丢失。

若无法转换,那么编译失败。

3. 返回值的自动转换

现在我们把返回值改为了double类型。但是,参数仍然保持int类型。

#include <iostream>
using namespace std;
double add(int a, int b){
 	return a+b;   
}
int main(){
    int c = add(1,2); 
    cout<<c;
    return 0;
}

ab相加的结果为int类型,返回时,会尝试将int转换为doubleint可以被转换为double,所以编译通过。

4. 代码封装成函数

相信你学完函数后心里肯定有这么一个疑问: 为什么要将代码封装成函数?

如果程序需要多次完成某项任务,那么你有两个选择:

1. 将同样的代码复制多份。

2. 将代码封装为一个函数,在需要的地方调用这个函数,提高代码的复用性。

显然,后者会更加便利于我们的日常开发工作。

5. 范例:海伦公式封装

接下来我们将演示,如何用代码求三角形面积。并探讨,将求三角形面积代码封装成函数的优点。

【问题描述】假设在平面内,有一个三角形,边长分别为a、b、c,三角形的面积S可由以下公式求得:

p = (a+b+c)/2

S = (p(p-a)(p-b)(p-c))½

这种求三角形面积的公式被称作海伦公式。

另外,三角形a,b,c任意两边的和大于第三边,否则无法构成一个三角形。即下列不等式需同时成立:

1. a + b > c

2. a + c > b

3. b + c > a

要求设计程序:从键盘中输入三个浮点数a、b、c,判断是否能组成三角形,如果能,输出这个三角形的面积。

【样例输入】3 4 5

【样例输入】It's a triangle

6

第一步, 首先我们应当判断三边能否构成一个三角形。

    if (a + b > c && a + c > b && b + c > a){
        cout<<"It's a triangle"<<endl;
    }
    else{
        cout<<"Not a triangle"<<endl;
    }

第二步, 根据三边abc求三角形面积。这里会遇到求一个数的平方根,我们可以使用函数 sqrt。若要使用 sqrt,需要包含头文件 math.h

p = (a + b + c) / 2;
s = sqrt(p * (p - a) * (p - b) * (p - c));

完整代码:

#include <iostream>
#include <math.h> //sqrt 需要包含头文件math.h
using namespace std;

int main(){
    double a,b,c,p,s;
    cin>>a>>b>>c;
    //判断能否构成三角形
    if (a + b > c && a + c > b && b + c > a){
        cout<<"It's a triangle"<<endl;

    }
    else{
        cout<<"Not a triangle"<<endl;
    }
    
    //海伦公式求三角形面积
    p = (a + b + c) / 2;
    s = sqrt(p * (p - a) * (p - b) * (p - c));
    //输出结果
    cout<<s<<endl;
    return 0;
}

输出结果:

在求三角形面积的代码中,可以被分成两个可复用部分:

1. 判断三边是否能构成三角形

2. 求三角形的面积

也许我们就是想知道三边能否构成三角形,而不需要求其面积,所以将其提取为一个函数。可以增加代码的复用性。

#include <iostream>
#include <math.h> //sqrt 需要包含头文件math.h
using namespace std;

//判断能否构成三角形的函数
void isTriangle (double a,double b,double c){
    if (a + b > c && a + c > b && b + c > a){
        cout<<"It's a triangle"<<endl;
    }
    else{
        cout<<"Not a triangle"<<endl;
    }
}

double areaOfTriangle(double a,double b,double c){
    //海伦公式求三角形面积
    double p,s;
    p = (a + b + c) / 2;
    s = sqrt(p * (p - a) * (p - b) * (p - c));
    //输出结果
    return s;
}


int main(){
    double a,b,c,p,s;
    cin>>a>>b>>c;
    //判断是否构成三角形
    isTriangle(a,b,c);
   
    //求三角形面积
    s = areaOfTriangle(a,b,c);
    cout<<s<<endl;

    return 0;
}

总结

  • 形参指代函数参数的类型,以及参数进入函数后,需要经历的处理步骤,没有确定值
  • 实参将确定形式参数的值具体是什么
  • 形参与实参若可以转换,那么将编译通过,转换过程中有可能出现数据丢失。 若无法转换,那么编译失败。
  • 代码封装成函数的意义:将代码封装为一个函数,在需要的地方调用这个函数,提高代码的复用性

第三节 函数的递归

本节内容

本节介绍在函数内部调用自身的过程

本节目标

  • 掌握函数递归调用
  • 掌握函数递归的条件
  • 掌握函数递推与回归过程

1.函数递归调用

函数需要被另一个函数调用才会执行,进一步发散思维,函数内部能否调用自己?

接下来我们写一个名为add的函数,然后在函数内部调用自己。

#include <iostream>
using namespace std;
void add(int a)
{
    cout<<a<<endl;
    add(a+1);
}

int main(){
    add(0);
    return 0;
}

输出结果:

编译成功,控制台中依次输出0、1、2、3.......

说明在C++中的函数内部是可以调用自己的,这种调用称为函数递归。

我们来分析一下add函数递归调用的过程:

    1. add函数在主函数main中被调用,传入参数0
    2. 进入add函数后,形参a的值为0acout语句输出
    3. a+1作为参数传入add函数,开始不断调用自己

由于add函数首尾相接,它将导致程序陷入死循环。就像一条衔尾蛇,头咬住了尾巴,整条蛇构成了一个闭环。

还记得怎么样手动打断程序执行吗?如果程序陷入了死循环,请使用Ctrl+c组合键结束程序。

如果你不打断程序执行,那么过不了多久,程序将出现栈溢出异常,导致程序异常结束

2.函数递归的条件

我们现在已经明白递归的特点了,很显然还不够,我们需要程序能正常结束。

add函数里已经有一个递推规则——每次a都增加1,那么我们还缺少什么呢? ?我们缺少一个递推结束条件 。

要完成递归函数的两个要素:

    1. 递推规则
    2. 递推结束条件

我们知道可以用break关键字结束循环,而要结束递推就要用return关键字,因为一旦函数执行到return,就会停止函数的执行,现在我们给程序加上递推结束条件,当a等于5时,递推结束。

#include <iostream>
using namespace std;

void add(int a){
    if(a==5){   // 递推结束条件
        return;
    }
    cout<<a<<endl;
    add(a+1);
}

int main(){
    add(0);
    return 0;
}

输出结果:

3. 递推与回归

a小于5之前,一直递推至下级函数;

a等于5时,从下级函数开始回归。

我们在add函数前后各放置一个cout语句,用于探究函数递归调用时的递推与回归过程。

#include <iostream>
using namespace std;

void add(int a){
    if(a==5){
        return;
    }
    cout<<"递推输出:"<<a<<endl;  //递推时输出
    add(a+1);
     cout<<"回归输出:"<<a<<endl;  //回归时输出
}
int main(){
    add(0);
    return 0;
}

输出结果:

程序执行后,先执行五次递推输出,值分别为0,1,2,3,4。之后,再执行五次回归输出,值分别为4,3,2,1,0。

下面的图中用红色线条画出了递推进入下级函数的流程,蓝色线条画出了从下级回归的流程。标号数字代表cout语句的执行顺序。

标号为①②③④⑤的cout语句在递推过程中依次执行,而标号为⑥⑦⑧⑨⑩的cout语句必须在回归过程执行, 由于回归过程与递推过程是逆向的,所以输出的a值是逆序的。

对于此 add函数,放在递归调用前的语句将在递推过程中执行,而放在递归调用后的语句将在回归过程中执行。

4.使用递归计算阶乘

一个正整数的阶乘是所有小于及等于该数的正整数的积,并且0的阶乘为1,负数没有阶乘。阶乘被记为 n!。

例如:

4! = 4*3*2*1

3! = 3*2*1

2! = 2*1

1! = 1

0! = 1

根据这个规律,我们还能将阶乘这样计算:

4! = 4*3!

3! = 3*2!

2! = 2*1!

1! = 1

0! = 1

得出递推规律:

    1. n为0或者1时,n的阶乘为1
    2. n大于1时,n的阶乘为n*(n-1)!

那么假设有这么一个函数 func(n) ,这个函数传入一个整数n,返回n的阶乘n!

递推结束条件:当n为0或者1时,func(n)返回1

递推规律:当n大于1时,func(n)=n*func(n-1)

#include <iostream>
using namespace std;

int func(int n){
    if(n==0 || n==1){
        return 1;
    }
    return n*func(n-1);
}

int main(){
    int r = func(4);
    cout<<r<<endl;
    return 0;
}



int func(int n){
    if(n == 8){
        return 1;
    }
    return func(n+1)+n;
}

int main(){
    int r = func(2);
    
    cout<<r<<endl;
    return 0;
}


总结

  • 在C++中的函数内部是可以调用自己的,这种调用称为函数递归
  • 完成递归函数的两个要素:递推规则和递推结束条件
  • 递归调用前的语句将在递推过程中执行,而放在递归调用后的语句将在回归过程中执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值