戳一戳!和我一起走进信息学的世界
导读
C++是大多数学生入门信息学、参加信息学竞赛必备的编程语言。掌握C++,能够有助于孩子未来工作发展,也能提升孩子的综合能力,如抗挫折能力、逻辑分析能力等。
上一篇文章,我们讲解了函数的基本知识,本篇文章中,让我们继续学习,深入了解一下函数的更多内容吧!
往期回顾
【NOIP竞赛/CSP认证】
▶ 赛前必看!信息学名师带你复习NOIP竞赛初赛及CSP认证初赛
【信息学精华帖】
▶ 信息学的万般好处!附C++必备基础知识总结
▶ 信息学提高班知识体系详解与家长常见问题解答!让孩子赢在提高班学习的起跑线!
【C++提高班教程】
▶ C++强化 | 01 新学期再出发!温故知新!
▶ C++强化 | 02 继续前行,三大结构终极介绍
▶ C++强化 | 03 一维数组入门
▶ C++强化 | 04 数组越界
▶ C++强化 | 05 一维数组经典应用
▶ C++强化 | 06 一篇文章带你掌握字符数组
▶ C++强化 | 07 二维数组
▶ C++强化 | 08 二维数组经典案例
▶ C++强化 | 09 一篇文章带你探索函数的奥秘
【C++基础班教程】
▶ C++总结 | 01 程序的世界
▶ C++总结 | 02 输出、换行与注释
▶ C++总结 | 03 变量定义、赋值与运算
▶ C++总结 | 04 算术运算符与赋值运算符
▶ C++总结 | 05 cin语句
▶ C++总结 | 06 程序中的数据类型
▶ C++总结 | 07 数据类型补充
▶ C++总结 | 08 顺序结构
▶ C++总结 | 09 if 和 if-else
▶ C++总结 | 10 if嵌套与逻辑运算符
▶ C++总结 | 11 开关语句switch-case
▶ C++总结 | 12 for循环及其应用
▶ C++总结 | 13 数据范围与数据类型
▶ C++总结 | 14 break与continue
▶ C++总结 | 15 while与do-while
▶ C++总结 | 16 循环嵌套及其应用
1 说在前面的话
从前面一节课,我们就开始进入一个新的领域——函数,今天这节课,我们将继续起航,探究函数内部更深的领域!
1 温故知新
当然啦,在学习之前,我们还是先复习一下旧的知识,打好基础,才能扬帆远航!
C++的函数可以帮助我们将一个小功能块封装起来。我们想要使用函数的时候,直接调用就可以啦!
一个函数,至少需要如下几个部分:
函数类型
函数名
函数体
1、对于函数类型
函数类型和变量的类型是一致的。包括:
整数相关类型:int, long, long long
小数相关类型:float, double
字符与字符串型:char, string
使用上述变量的时候,函数需要有返回值,返回值的类型必须与函数的类型一致。
但是一般变量没有void变量,函数有void函数,表示函数返回空。如果使用void类型,我们一般不用写返回值。
2、对于函数名
函数名和变量名的命名规则是类似的。例如:
不能使用关键字;
只能由数字、字母和下划线组成,数字不能放开头。
但是同一个函数名可以多次定义,我们称之为函数名的重载。但是函数名多次定义的时候,有很多限制条件,在我们强化班,先不考虑,所以我们先默认函数名不能多次定义。
3、对于函数体
函数的具体内容,都写在函数体里面。调用函数的过程,就是执行函数体里面代码的过程。
除了上述部分,函数可能还需要参数,我们对参数的总结如下:
参数是写在小括号里面的;
参数要声明类型;
参数如果有多个,就需要用逗号隔开,并且每个参数名不能相同;
参数一定要在函数体中应用,不然就没有声明的必要了。
了解这些知识,老师提个问题:函数的作用是什么呢?
我们前面的示例中,函数都是多此一举的:
#include<iostream>
using namespace std;
int add(int a, int b){
return a+b;
}
int main(){
int c = add(5, 3);
cout<<c<<endl;
return 0;
}
对于上面的代码,我们完全可以用如下方式去写,更加简洁:
#include<iostream>
using namespace std;
int main(){
int c = 5+3;
cout<<c<<endl;
return 0;
}
主要出自于两方面的考虑:
第一方面,具有相同功能的代码块,如果不用函数,就要多次编写代码。如果我们使用函数,将代码块写在函数中,那我们每次使用的时候,直接调用函数就可以了,更加方便。
例如:我们要扩大两个数字到一定的的倍数。如果不用函数,我们需要这样写:
#include<iostream>
using namespace std;
int main() {
int a,b,c,d,e;
cin>>a>>b>>c>>d>>e;
a*=c;
b*=c;
cout<<"a扩大"<<c<<"倍为:"<<a<<endl;
cout<<"b扩大"<<c<<"倍为:"<<b<<endl;
cout<<"a+b为:"<<a+b<<endl;
d*=c;
e*=c;
cout<<"d扩大"<<c<<"倍为:"<<d<<endl;
cout<<"e扩大"<<c<<"倍为:"<<e<<endl;
cout<<"a+b为:"<<d+e<<endl;
return 0;
}
如果使用函数就可以这样写:
#include<iostream>
using namespace std;
void add(int a, int b, int c){
a*=c;
b*=c;
cout<<"a扩大"<<c<<"倍为:"<<a<<endl;
cout<<"b扩大"<<c<<"倍为:"<<b<<endl;
cout<<"a+b为:"<<a+b<<endl;
}
int main() {
add(5, 3, 2);
add(6, 8, 7);
return 0;
}
第二方面,后续代码越来越多,如果把所有的代码都写在主函数中,就看着很臃肿,也很难看明白代码的功能。如果我们把不同的代码块,封装成不同的函数,在主函数中只负责调用其他函数,整体就会非常简洁。第二方面在后续的学习中,大家会越来越有体会。
所以,一些可以封装成函数的代码块,我们尽可能的写成函数的形式,一方面为了后续使用方便,一方面能让我们更方便去理解程序。
掌握了上面这些,我们就可以更深入了解函数啦,让我们一起走进今天的新知识吧!
2 函数的定义位置
我们前面讲的所有定义函数都是在主函数前面定义的,我们可以在主函数后面定义吗?
1 定义在后面行不行?
我们尝试将函数定义在主函数后面:
#include<iostream>
using namespace std;
void print(){
cout<<"hello AI与区块链技术"<<endl;
}
int main() {
print();
add(5, 3);
return 0;
}
void add(int a, int b){
cout<<a+b<<endl;
}
执行出现了如下问题:
[Error] 'add' was not declared in this scope
这是因为:
程序是从前往后,从上往下,顺序执行的。如果我们在后面定义的话,因为我们已经执行了main函数,但是我们还没有执行到我们自己 定义的add函数,计算机找不到该函数的定义,就会报错。
2 先声明,后定义
我们可以采用如下的写法,将函数的声明和函数的定义分开:
#include<iostream>
using namespace std;
void print(){
cout<<"hello AI与区块链技术"<<endl;
}
void add(int a, int b);
int main() {
print();
add(5, 3);
return 0;
}
void add(int a, int b){
cout<<a+b<<endl;
}
其中函数的声明就相当于用分号代替了原来的大括号和大括号里面的函数体。就是告诉计算机,我们现在有这样一个函数,函数的声明,必须写在函数的调用之前。具体的实现可以写在其他地方:
void add(int a, int b);
函数的具体定义为:
void add(int a, int b){
cout<<a+b<<endl;
}
函数的具体定义,既可以写在主函数上面(必须写在该函数声明的下面,否则声明无意思。),也可以写在主函数的下面。
这样,我们就可以把函数的具体定义写在主函数的后面啦!
3 局部变量与全局变量
我们前面一直讲变量,但是我们的变量都是在main函数里面定义的!
1 局部变量
我们尝试在自己写的函数里面定义变量,然后在主函数中调用:
#include<iostream>
using namespace std;
void add(int a, int b){
int c = a+b;
}
int main() {
add(5, 3);
cout<<c<<endl;
return 0;
}
程序会报错如下:
[Error] 'c' was not declared in this scope
这是因为,c变量是我们在自己的函数体里面写的,那这个变量也只能在这个函数体中应用,一旦出了这个函数体,这个变量就会被计算机释放掉,其他的代码就找不到这个变量了。也就是说这个变量只能在局部应用。我们把这种变量称之为局部变量。
2 全局变量
如果我们想让所有函数都能应用同一个变量,我们就需要用到全局变量:
全局变量是那些能被所有的程序块,执行并且不会出错的变量。全局变量可以在程序全局任何位置被调用。
一个完整的项目是由多个cpp文件构成的,对全局变量的要求很高。但对我们来说,我们的代码只在一个程序中,我们全局变量只需要定义在所有函数的上面即可,例如:
#include<iostream>
using namespace std;
int c; //c是全局变量
void add(int a, int b){
c = a+b;
}
int main() {
add(5, 3);
cout<<c<<endl;
return 0;
}
3 局部与全局的对比
我们了解了全局变量和局部变量的概念,我们来对比一下。
首先最重要的就是作用域不同,局部变量只能在定义的局部范围使用,全局变量能在程序的任意位置使用(当然得在定义之后使用)。
其次,我们局部变量和全局变量的初值可能是不同的:
#include<iostream>
using namespace std;
int a;
int main() {
int b;
cout<<a<<endl;
cout<<b<<endl;
return 0;
}
全局变量的初值一定为0,局部变量的初值是一个随机值,有可能为0,也有可能为其他值。
最后,局部变量和全局变量的生命周期不同。全局变量在程序执行过程中一直占用内存单元,生命周期一直到程序结束才结束,全局变量的值可以一直保留到程序结束。而局部变量的内存单元是临时分配的,当函数执行完成,局部变量的内存单元就被释放,生命周期停止,局部变量的值只能保留到函数应用过程中,结束就不存在了。
总结一下就是有下面三个不同:
作用域不同:局部变量的作用域是所在函数中,全局变量的作用域是整个程序;
初始值不同:局部变量的初值为随机值,全局变量的初值为0;
生命周期不同:局部变量的生命周期是所有函数的周期,全局变量的生命周期是整个程序的周期。
3 参数拓展
大家可能会发现,我们在定义过程中,会用到参数,在使用过程中会用到参数,参数对于函数是非常重要的,让我们继续深入,了解更多参数的相关知识吧!
1 形参与实参
在函数的定义过程中,我们需要定义函数的参数,来“占坑”,告诉计算机,我们这个函数需要用到这些变量,这些参数是形式上的参数,我们称之为形参;
在具体的调用过程中,我们使用实际的参数来使用函数,我们称之为实参。
总结一下就是,在函数声明与定义过程中的参数是形参,在函数的具体调用过程中是实参。
2 函数参数的引用调用
我们写如下函数,我们在主函数中调用:
#include<iostream>
using namespace std;
void change(int a){
a = 6;
}
int main() {
int a = 5;
change(a);
cout<<a<<endl;
return 0;
}
我们发现执行完函数之后,我们的变量的值没有发生变化,我们定义函数的时候,如果参数是上面这种定义方式,不会改变参数的值。
如果我们想通过函数修改变量的值怎么办呢?
我们可以在函数的参数前面加一个&符号,这个是函数参数的引用调用,会改变函数参数的值。
我们的函数如下:
#include<iostream>
using namespace std;
void change(int &a){
a = 6;
}
int main() {
int a = 5;
change(a);
cout<<a<<endl;
return 0;
}
这样我们的输出结果就是6了。
我们只需要注意,如果我们希望在执行程序的过程中改变参数的值,就需要使用引用调用,否则,只使用普通调用即可。
4 习题
用手机发短信,一条短信费为0.1元,但限定一条短信的内容在70个字以内(包括70个字)。如果你一次所发送的短信超过了70个字,则会按照每70个字一条短信的限制把它分割成多条短信发送。假设已经知道你当月所发送短信的次数与字数,试统计一下你当月的短信总资费。
【输入描述】
第一行是整数n,表示当月发送短信的总次数;
接着n行每行一个整数,表示每次短信的字数;
【输出描述】
一行,当月短信总资费,单位为元。
【输入示例】
10
39
49
42
61
44
147
42
72
35
46
【输出示例】
1.3
AI与区块链技术
长按二维码关注