一、第一个C++程序
#include<iostream>
using namespace std;
int main() {
cout << "Hello World!";
return 0;
}
二、数据类型、变量与常量、运算符
2.1 数据类型
2.2 变量与常量
2.3 运算符
三 、判断语句(if-else、switch-case)
3.1 if语句
if语句:用来实现两个分支的选择结构。
3.1.1 if语句的一般形式
//1.没有else子句部分
if(表达式) 语句1
//2.有else子句部分
if(表达式){
语句1
}else{
语句2
}
if 语句基本形式是:if (表达式)语句,“表达式”值可以是任意合法的数值。
3.1.2 级联的if-else语句
示例:交电费,分阶段收费
#include<iostream>
using namespace std;
int main() {
float x = 0.0;
float y = 0.0;
cin >> x;
if (x <= 100) y = x * 0.5;
else if (x > 100 && x <= 300) y = 100 * 0.5 + (x - 100) * 0.8;
else if (x > 300 && x <= 1000) y = 100 * 0.5 + 200 * 0.8 + (x - 300) * 1.2;
else y = 100 * 0.5 + 200 * 0.8 + 700 * 1.2 + (x - 1000) * 2;
cout << y;
return 0;
}
3.1.3 嵌套的if-else语句
示例:找出三位数中的最大值?
#include<iostream>
using namespace std;
int main() {
int a, b, c;
int max = 0;
cin >> a >> b >> c;
if (a > b) {
if (a > c) {
max = a;
}
else {
max = c;
}
}
else {
if (b > c) {
max = b;
}
else {
max = c;
}
}
cout << max;
return 0;
}
3.2 switch语句
switch语句:实现多分支选择结构。
其基本语句为:
switch(控制表达式)
{
case 常量:
语句;
break;
default:
语句;
break;
}
示例:根据月份数输出对应月份的英文
#include<iostream>
using namespace std;
int main() {
int month;
cin >> month;
switch (month) {
case 1:cout << "January" << endl; break;
case 2:cout << "February" << endl; break;
case 3:cout << "March" << endl; break;
case 4:cout << "April" << endl; break;
case 5:cout << "May" << endl; break;
case 6:cout << "June" << endl; break;
case 7:cout << "July" << endl; break;
case 8:cout << "August" << endl; break;
case 9:cout << "September" << endl; break;
case 10:cout << "October" << endl; break;
case 11:cout << "November" << endl; break;
case 12:cout << "December" << endl; break;
}
return 0;
}
3.3 关系、逻辑与条件运算符与表达式
3.3.1 关系运算符与表达式
例如:a>3是一个关系表达式,>是一个关系运算符。
关系运算符:
运算符 | 含义 | 举例 | 说明 |
---|---|---|---|
> | 大于 | a>b | a>b 则true,否则false |
>= | 大于等于 | a>=b | a>=b则true,否则false |
< | 小于 | a<b | a<b 则true,否则false |
<= | 小于等于 | a<=b | a<=b则true,否则false |
== | 等于 | a==b | a=b 则true,否则false |
!= | 不等于 | a!=b | a!=b 则true,否则false |
括号、关系运算符与算术运算符的优先级:
括号() | *、/、% | +、- | <、<=、>、>= | ==、!= |
优先级高 ———————————————————> 优先级低 |
3.3.2 逻辑运算符与表达式
运算符 | 含义 | 举例 | 说明 |
---|---|---|---|
&& | 逻辑与(and) | a&&b | 全真则真,一假全假 |
|| | 逻辑或(or) | a||b | 一真全真,全假才假 |
^ | 异或运算符 //严格来说,这是位运算符, 但它常常用于逻辑判断,故 放在这里。 | a^b | 一真一假则为真, 全真全假则为假 |
! | 逻辑非(not) | !a | 假则为真,真则为假 |
运算符优先级:
括号() | !、-(负号)、++、-- | *、/、% | +、- (加减运算) | <<、>> (左右位移) | <、<=、>、>= | ==、!= | && | || |
优先级高 ———————————————————> 优先级低 |
3.3.3 条件运算符与表达式
运算符 举例 说明 表达式1?表达式2:表达式3 (a>b)?a:b 如果表达式1为真,取值表达式2;为假,取值表达式3
3.4 分支结构的测试:程序测试方法
什么是测试?(找逻辑上的bug)
- 指对一个完成了全部或部分功能、模块的计算机程序在正式使用前的检测,以确保该程序能按预定的方式正确地运行。
- 是程序员开发人员或程序测试人员的任务。
- 通过运行测试用例,找出程序中尽可能多的Bug。
程序测试方法的分类:
- 白盒测试(结构测试):程序员自己看代码,主要用于测试的早期和重要的路径。
- 黑盒测试(功能测试):代码交给系统测试,主要用于测试的后期和重要的功能。
四、循环语句(for、while、do-while)
选择循环语句的一般原则:循环次数已知——for语句
循环次数未知——while语句
循环体至少执行一次——do-while语句
4.1 for循环
for语句能用于两种情况:1.循环次数已经确定。2.循环次数不确定而只给出循环结束条件。
其基本语法如下:
for(循环变量赋初值;循环条件;循环变量增值){
//循环语句
}
示例:求1~100的和(注意到,sum前面不加“&”)
#include<iostream>
using namespace std;
int main(){
int sum=0;
int i;
for(i=1;i<=100;i++){
sum=sum+i;
}
cout<<"1~100的和为"<<sum;
return 0;
}
/*为什么printf里面的变量sum不加“&”?
加&传递的是指针,是一个内存地址;不加&传递的是值,是一个变量的拷贝。
printf 在输出时,只需要值就够了。
而scanf 之所以加&,是因为他需要知道你想把输入的参数,保存在内存的什么地方,也就是你给的变量的地址*/
4.2 while循环
while语句:只要当循环条件表达式为真(即给定的条件成立),就执行循环体语句。
(while循环的特点:先判断循环条件,后执行循环体语句)
其基本语法如下:
while(循环条件){
//循环体
}
示例:求1~100的和
#include<iostream>
using namespace std;
int main(){
int i=1,sum=0;
while(i<=100){
sum=sum+i;
i++;
}
cout<<"1~100的和为"<<sum;
return 0;
}
4.3 do-while循环
do-while语句:先执行循环体,然后再检查循环条件是否成立,若成立再执行循环体。
(do-while循环的特点:先无条件执行循环体,然后判断循环条件是否成立。)
其基本语法如下:
do{
//循环体
}while(循环条件)
示例:求1~100的和
#include<iostream>
using namespace std;
int main(){
int i=1,sum=0;
do{
sum=sum+i;
i++;
}while(i<=100);//注意在这里,有一个分号!!
cout<<("1~100的和为"<<sum;
return 0;
}
4.4 改变循环执行的状态
4.4.1 break语句提前终止循环
break语句的作用:使流程跳出循环体之外,接着执行循环体下面的语句。
(break语句只能用于循环语句和switch语句之中,而不能单独使用。)
示例:1~100从小到大依次相加,当和大于3000时,立刻输出和。
#include<iostream>
using namespace std;
int main(){
int sum=0;
for(i=1;i<=100;i++){
sum=sum+i;
if(sum>3000) break;//break语句前常用if语句作为执行条件
}
cout<<"此时和为"<<sum;
return 0;
}
思考:如果是双重循环,在内循环体内有一个break语句,下一步进行什么循环?
解答:提前终止内循环,继续进行外循环。
4.4.2 continue语句提前结束本次循环
continue语句的作用:结束本次循环,进入下一次循环。
示例:要求输出100~200不能被3整除的数。
#include<iostream>
using namespace std;
int main() {
int i;
for (i = 100; i < 200; i++) {
if (i % 3 == 0) //i%3==0,是指i除以3的余数等于0,即i能够被3整除
continue; //continue语句也常搭配if语句进行使用
cout<<i<<endl;
}
return 0;
}
4.5 递推法
4.5.1 通过Fibonacci数列看递推法
斐波那契数列是什么?
斐波那契数列是一个数列,以0和1开始,后面的每一项都是前两项的和。数列的前几项如下:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
#include<iostream>
using namespace std;
int F(int num);
int main() {
int i;
cin >> i;
F(i);
cout << F(i);
return 0;
}
int F(int num) {
if ((num == 0) || (num == 1)) {
return num;
}
else {
return F(num - 1) + F(num - 1);
}
return 0;
}
4.5.2 递推法的基本思想
- 递推法:指从问题的已知条件出发,依据某种递推关系,依次推出所要求的各中间结果及最后结果。
- 递推初始条件确定:问题本身已经给定or通过对问题的分析与化简后确定。
- 递推的方向(两种):顺推法、逆推法
顺推法:从已知初始条件出发,通过递推关系逐步推算出要解决的问题的结果的方法。如求斐波那契数列。
倒推法:在不知初始值的情况下,经某种递推关系而获知于问题的解或目标,从这个解或目标出发,采用倒推手段,一步步地倒退到这个问题的初始情况。如猴子吃桃。
实现递推的步骤:
- 确定递推变量:要根据问题的具体实际,设置递推变量。
- 建立递推关系:递推关系是指,如何从变量的前一些值推出其下一个值,或从变量的后一些值推出其上一个值的公式(或关系)。
- 确定初始(边界)条件:对确定的递推变量,要根据问题最简单情形的数据确定递推变量的初始(边界)值,这是递推的基础。
- 对递推过程进行控制:递推过程不能无休止地重复执行下去。递推过程在什么时候结束,满足什么条件结束,这是编写递推算法必须考虑的问题。
4.6 穷举法
4.6.1 什么情况下用穷举算法
- 穷举法:根据问题中的“约束条件”,将所有可能的解一一列举出来,并逐个验证是否符合“约束条件”,找出符合要求的解。
- 穷举法适合求解的问题:可能的答案是有限个and答案是已知的,但又难以用解析法描述。这种算法通常用循环结构来完成。
4.6.2 百鸡问题
“百鸡问题”:鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一。百钱买百鸡,问鸡翁、母、雏各几何?
#include<iostream> using namespace std; int main() { int i, j, k; for (i = 1; i <= 20; i++) { for (j = 1; j <= 33; j++) { for (k = 1; k <= 300;k++) { if ((i + j + k == 100) && (5 * i + 3 * j + k / 3 == 10)) { break; } } } } cout << i << " " << j << " " << k << endl; return 0; }
优化,两重循环(k=100-i-j)
#include<iostream> using namespace std; int main() { int i, j,k; for (i = 1; i <= 20; i++) { for (j = 1; j <= 33; j++) { k = 100 - i - j; if ((5*i + 3*j + k/3 == 100) && ( k % 3 == 0)) { break; } } } cout << i << " " << j << " " << k << endl; return 0; }
4.7 循环程序调试方法:单步调试法
程序调试的步骤:猜测出错位置,设置断点——>单步运行——>观察变量——>发现问题——>修正代码,重新运行——>解决问题
循环程序常用断电:初始值,改变程序条件变量的位置,判断条件。
五、数组
5.1 一维数组
定义一维数组:数据类型 数组名[常量表达式];
如int a[10],char c[10]。
- 数组是用来存储相同数据类型的数据结构。
- 数组在内存里有一块连续的存储空间(区别于链表)。
- 常量表达式是来表示数组大小的,定义之后就不能改变。
5.2 二维数组
定义二维数组:数据类型 数组名[常量表达式1][常量表达式2];
如int a[][10],char c[][10]。
- 二维数组可以表示二维表格上的数据。
- 二维数组存储形式先行后列。
- 二维数组的处理一般采用双重循环语句。
5.3 字符串与字符数组
解决两个问题:
- 字符串输入cin和cin.getline区别
- 字符串输出如何不出现乱码?
字符串与字符数组的不同
字符串char name[]="hust";//数组长度为5
h u s t \0 字符数组char name[]={'h','u','s','t'};//数组长度为4
h u s t
字符数组的输入输出
在实际应用cin输入字符数组时,会自动在输入的字符尾端加上'\0',故在定义数组大小时应考虑到这一点。
- 字符数组的输入(cin、cin.getline)
用"cin>>数组名"的形式输入
遇到空格,Tab,回车键表示数据之间间隔;回车键还可以表示输入结束
用"cin>>数组名"输入单个字符串时,其中不能有空格
用"cin>>数组名"输入多个字符串时,可以用空格分隔
用cin.getline函数(输入字符串中包含空格字符)
cin.getline(字符数组名,字符个数n)
输入回车表示输入结束
输入字符的数量(不包含回车)最大为n-1
输入的字符后会自动添加串结束符'\0',作为字符串的一部分放入数组
如果输入字符的数量(不包含回车)大于n-1个,将前n-1个字符放入数组
cin和cin.getline混合使用
运行下面这个代码会发现
cin,getline语句被直接跳过,并没有输入的过程。这是因为cin>>和cin.getline从缓冲区读数据时的方式不同,cin>>不读"\n"换行符,但是cin.getline读"\n"换行符,那么在实际的运行过程中代码读完cin>>之后会立马遇到一个换行符,cin.getline 也就没运行就结束了。那么如何解决上述问题?(清空缓冲区)
混合使用cin和cin.get.line时,用cin.clear()和cin.sync()解决问题
//混用之后,有问题的代码 #include<iostream> using namespace std; int main() { char ch[5]; int number; cin >> number; cin.getline(ch, 5); return 0; } //清空缓冲区,没问题的代码 #include<iostream> using namespace std; int main() { char ch[5]; int number; cin >> number; cin.getline(ch, 5); return 0; }
- 字符数组的输出
cout遇到'\0'才能结束
//没有结束符,有问题的代码 #include<iostream> using namespace std; int main() { char a[4] = "C++"; char b[3] = { 'c','+','+' }; cout << a << endl; cout << b << endl; return 0; } //加结束符,没问题的代码 #include<iostream> using namespace std; int main() { char a[4] = "C++"; char b[3] = { 'c','+','+' ,'\0'}; cout << a << endl; cout << b << endl; return 0; }
5.4 字符串的应用
字符数组与数值数组之间的异同
- 相同点:定义格式一样,可以使用循环访问每个元素。
- 不同点1:循环控制条件不同
int a[6] = { 1,2,3,4,5,6 }; for (int i = 0; i < 6; i++) { cout << a[i]; } char b[60] = "hello"; for (int j = 0; b[i] != '\0'; j++) { cout << b[j]; }
- 不同点2:字符数组可以使用数组名整体访问整个字符串
//输出的是数组a的首地址 int a[6] = { 1,2,3,4,5,6 }; cout<<a<<endl; //输出b为首地址的字符串,直至遇到'\0'结束 char b[60] = "hello"; cout << b<<endl;
六、指针——C++的灵魂
6.1 指针的概述及基本应用
6.1.1 指针的基本概念
- 内存地址:计算机的内存被划分为一个个的存储单元,简称内存单元。内存单元按一定的规则编号,这个编号就是存储单元的地址。
- 变量与地址:程序中定义的变量要占用一定的内存空间,不同的数据类型的变量占用的内存空间大小不一样。如整型变量占用4个存储单元,浮点型变量占用8个存储单元。
- 访问内存中的数据(两种方式:直接访问,间接访问)
直接访问:通过变量名,直接对变量的存储空间进行存取访问。
间接访问:根据变量的地址进行存取访问。
指针变量与指针变量的定义
- 指针变量是一种特殊的变量,用于存放内存单元的地址,即
能存放地址的变量就是指针变量。- 指针变量定义的一般形式:数据类型 *指针变量名;
定义语句中的 *表示该变量为指针变量。
指针变量前的数据类型规定了指针变量指向的变量的数据类型。
如double *p,p为指针变量,且指针变量p里存放着double类型变量的地址。
- 指针变量的三要素
变量名:与一般变量取名相同,数字、字母、下划线组成,字母、下划线开头。
指针变量的类型:
指针所指向的变量的类型,而不是自身的类型。指针变量本身均为long int类型。指针变量的值:是某个变量的内存地址。
6.1.2 与指针相关的运算符&、*
取地址运算符:&
- &是一个一元运算符,用来得到一个变量的地址。&后面必须是个变量。
- 例如int a;
&a表示变量在内存中的
起始地址,
指针运算符:*
- *是一个一元运算符,表示指针变量所指向的变量的值(取值)。*后面必须是个地址。
注意:*在不同地方出现含义不同。
- 如:int a=5,b,*p;
p=&a;//*p也就是取地址&a内存放的变量a的值
b=*p;//(即p所指向的内存单元的内容),等价于b=a
6.1.3 指针变量的初始化与赋值
- 指针变量初始化,在定义时赋值:数据类型 *指针名=地址
- 指针赋值: 指针变量=地址
其中的“地址”可以是变量的地址、数组名等。
如:int x=8;
int *p=&x;//指针变量初始化
int *q;
q=p;//指针赋值
- 不能把常量或表达式赋给指针变量。
p=&67; p=&(i+4)是非法的
- 不能将一个整数赋给指针变量,但可以赋整数值0,0是可以直接赋给指针变量的唯一整数值,表示空地址(NULL)。
int *p; p=0;//p为空指针,不指向任何地址
6.2 指针与一维数组
6.2.1 指针表示一维数组的元素
int a[10];
对于一维数组a:
- 数组名a表示数组的首地址,即a[0]的地址。
- 数组名a是地址常量
- a+i是元素a[i]的地址
- a[i]==*(a+i)
数组:int a[]={1,2,3,4,5,6,7,8,9};
a[0] 下标表示法 *(a+0) 地址表示法 a[1] *(a+1) a[2] *(a+2) a[3] *(a+3) 数组:int a[]={1,2,3,4,5,6,7,8,9};
int *p=a;
cout<<*(p+0) 下标表示法 cout<<p[0] 地址表示法 cout<<*(p+1) cout<<p[1] cout<<*(p+2) cout<<p[2] cout<<*(p+3) cout<<p[3]
6.2.2 指针的算术运算与比较运算
int a[] = { 1,2,3,4,5,6,7,8,9 }; int* p = a;
此时p是指针变量里面存放着&a,p指向a[0]
- p++后,p指针指向的地址是什么?
- p--后,p指针指向的地址是什么?
- 若有语句,int *p1=&a[1],*p2=&a[4];p2-p1的意义是什么?
- p1与p2可以比较大小吗?
对应的回答如下:
- p是一个指针变量。其中存储着&a也就是&a[0](数组的首地址)。当p++后,p指向&a[1]。此时p=&a[1],*p=2。
- 与1类似。p--也可以使用,但此时不能将int *p=a,应该int *p=&a[n](n>0)。举个例子,若int *p=&a[1],p--后,p指向&a[1]。此时p=&a[0],*p=1。
- p2-p1意味着:两只指针之间的数据个数。此时p2-p1=3。注意p2+p1没有意义。
- p2>p1。这里比较的其实是&a[1]与&a[4],根据数组的数据存放规律,可以知道&a[4]>&a[1],即p2>p1。
6.3 指针与二维数组
6.3.1 二维数组与指针的关系
int a[3][4]
对于二维数组a:
- a是数组名,包含三个元素,a[0],a[1],a[2]
- 每个元素a[i],又是一个一维数组,包含4个元素
- 二维数组具有二级地址的含义
指针与二维数组之间的关系
指针(地址) 数组 指针(元素值) 数组 *(a+0)
a[0]
//第一行
*(*(a+0)+0)
a[0][0]
//第一行的第一个元素
*(*(a+0)+1) a[0][1] *(*(a+0)+2) a[0][2] *(*(a+0)+3) a[0][3] *(a+1)
a[1] *(*(a+1)+0)
a[1][0] *(*(a+1)+1) a[1][1] *(*(a+1)+2) a[1][2] *(*(a+1)+3) a[1][3] *(a+2) a[2] *(*(a+2)+0)
a[2][0] *(*(a+2)+1) a[2][1] *(*(a+2)+2) a[2][2] *(*(a+2)+3) a[2][3] a——行地址,数组的首地址,第0行的首地址
a+i——行地址,第i行的首地址
a[i]=>*(a+i)——列地址,第i行第0列的元素地址
a[i]+j=>*(a+i)+j——列地址,第i行第j列的元素地址
*(a[i]+j)=>*(*(a+i)+j)——a[i][j]第i行第j列的元素值
6.3.2 行指针与列指针
指向二维数组元素的指针变量——列指针
#include<iostream> using namespace std; int main() { int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} }; int * p; for (p = a[0]; p < a[0] + 12; p++) { cout << *p << " "; } cout<<endl; return 0; }
指向一维数组的指针变量——行指针
- 行指针的定义格式:数据类型 (*指针名)[一维数组长度]
- 如:int (*p)[4];
#include<iostream> using namespace std; int main() { int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} }; int(*p)[4]; int i, j; for (i = 0, p = a; i < 3;i++,p++) { for (j = 0; j < 4; j++) { cout << *(*p + j) << " "; } } cout << endl; return 0; }
6.4 指针数组
6.4.1 指针数组的定义、赋值和初始化
- 定义格式:数据类型 *数组名[数组长度说明];
int *p[4];//应该区分于int (*p)[4];行指针
- 指针数组赋值与初始化
//指针数组赋值 int a[2][3]; int* p[2]; p[0] = a[0]; p[1] = a[1]; //指针数组初始化 int a[2][3]; int* p[] = { a[0],a[1] }; //字符型的指针数组初始化 char* p[] = { "chinese","people","good",NULL };
6.4.2 二维数组与指针数组的区别
//二维数组 char name[5][9] = { "gain","much","stronger","point","bye" }; //指针数组 const char* name[5] = { "gain","much","stronger","point","bye" };
- 内存的分配不同(二维数组(左),指针数组(右))
- 用指针数组处理二维数组
#include<iostream> using namespace std; int main() { int a[2][3] = { {1,2,3},{4,5,6} }; int* p[] = { a[0],a[1] }; for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++, p[i]++) { cout << *p[i] << " "; } cout << endl; } return 0; }
6.4.3 数组指针与指针数组的区别
- 数组指针:本质是个指针,指向数组的指针。int (*p)[10];
- 指针数组:本质是个数组,数组里面装着指针。int *p[10];
6.5 多级指针、动态内存分配
6.5.1 多级指针的定义、赋值与初始化
- 定义:指向指针的指针
- 一级指针:指针变量中存放目标变量的地址。
- 多级指针:指针变量中存放一级指针变量的地址。
6.5.2 动态内存分配
动态存储分配:程序需在运行时根据需要分配相应大小的存储空间。
- 格式规范:指针变量名=new 数据类型名(初值列表);
//一行直接动态内存分配 int *point=new int[2]; //分两行动态内存分配 int *point; point=new int[2];
- 动态内存释放:delete 指针名;
delete point;
- 动态内存分配是否成功的判断语句?
int* point = new int(10); if (point == 0) { cout << "Error!" << endl; return -1; }
动态数组:
- 动态数组解决什么问题?定义数组时元素个数还不能确定。
- 动态数组如何定义?
指针变量名=new 数据类型名[下标表达式];//下标表达式可以使用变量
delete[] 指针变量名
- 如何访问各数组元素?
由上式定义的是无名数组,其元素只能通过指针变量间接访问。
#include<iostream> using namespace std; int main() { int n; cin >> n; int* p = new int[n]; //顺序输入数组元素,逆序输出数组元素 for (int i = 0; i < n; i++) { cin >> p[i]; } for (int j = n - 1; j > -1; j--) { cout << p[j] << " "; } cout << endl; delete[] p; return 0; }
七、函数——面向过程的程序设计核心
7.1 函数基础知识
本章中关于函数的知识大多沿用C语言使用函数的方法,在学习记录(面向对象篇)中main函数以外的函数大多是被封装在类中,主函数或其他函数可以通过类对象调用类中的函数。
- 函数的分类:从用户使用角度看,分为系统函数和用户自定义的函数。从函数的形式来看,分为有参函数和无参函数。
- 区别形参和实参
1.实参对形参的数据传递是“单向值传递”,仅能单向由实参——>形参。如需要从函数中传递值,可以使用返回值和指针。
2.形参和实参的数据类型应一致或赋值兼容。
- 返回值:函数不一定都有返回值,也有无返回值的函数。
//有返回值的函数调用 num=point(a,b); //无返回值的函数调用 point(a,b);
7.2 函数的调用
7.2.1 函数调用的一般形式
一般形式:函数名([实参序列])
如调用无参函数,则“实参序列”可以没有,但圆括号不能省略。
函数调用的条件:
- 被调用函数必须是已存在的函数(库函数或用户自定义的函数)。
- 使用库函数,需要加头文件。如#include<cmath>、#include<iostream>
- 使用用户自定义的函数,需要加函数声明。函数声明最常用的方式就是函数原型,用于告诉编译器函数的返回类型、函数名和参数列表。如int point(int a,int b);、 void prt(double a);
7.2.2 函数的嵌套调用
C/C++中不能嵌套定义函数,但可以嵌套调用函数,即调用一个函数的过程中又调用另一个函数。
举例说明:输入4个整数,找出其中最大值。
#include<iostream> using namespace std; int max_4(int a, int b, int c, int d); int max(int x, int y); int main() { int a, b, c, d, max; cin >> a >> b >> c >> d; max=max_4(a, b, c, d); cout << max << endl; return 0; } int max_4(int a, int b, int c, int d) { int m; m = max(a, b);//函数的嵌套调用 m = max(m, c); m = max(m, d); return m; } int max(int x, int y) { if (x > y) { return x; } else { return y; } }
7.2.3 函数的递归调用
递归调用:在调用一个函数的过程中,又出现直接或间接地调用该函数本身。C/C++军允许函数的递归调用。
举例说明:用递归方法求n!
#include<iostream> using namespace std; long long fac(int num); int main() { int num; cin >> num; cout << fac(num); return 0; } long long fac(int num) { long long f; if (num < 0) { cout << "Error!" << endl; f = -1; } else if (num == 0 || num == 1) { f = 1; } else { f = fac(num - 1) * num;//函数的递归调用 } return f; }
7.3 内置函数
在前面讲解函数调用的条件部分,我有提到函数声明其实不止函数原型这一种方式,只是函数原型这种方式更常见。但在实际应用中,为了减少函数调用的时间开销,往往不使用函数原型,而是使用内置函数。
- 内置函数:使用关键字
inline
来声明内置函数,编译器会将内置函数的代码复制到调用处,以减少函数调用的开销。- 内置函数的使用场景:内置函数可以节约运行时间,但会增加目标程序的长度。故只将规模很小(5个语句以下)而使用频繁的函数(如定时采集数据的函数)声明为内置函数。
举例说明:对比a,b,c三个数的大小,并输出最大值。
#include<iostream> using namespace std; inline int max(int a, int b, int c);//声明内置函数,注意左端有inline int main() { int a, b, c; cin >> a >> b >> c; cout << max(a, b, c) << endl; return 0; } inline int max(int a, int b, int c) {//定义max为内置函数 if (b > a)a = b; if (c > a)a = c; return a; }
7.4 函数的重载
函数进入程序世界的初始就是为了实现程序模块化,也就是一个函数对应一种功能。但有时我们要实现的是同一功能,只是部分细节有所不同。使用多个不同的函数名,不利于程序的可读性。这就要用到函数的重载:
- 函数的重载:用同一函数名定义多个函数,而这些函数的参数个数和参数类型可以不相同。(C语言中不适用,C++中适用)
- 重载函数的参数个数、参数类型或参数顺序三者中必须至少有一种不同。无法重载仅按返回类型区分的函数。
举例说明:求3个数中的最大数。(分别考虑整数,双精度数,长整数这三种情况),用函数重载方法
#include<iostream> using namespace std; int max(int a, int b, int c); double max(double a, double b, double c); long max(long a, long b, long c); int main() { int a, b, c; double x1, x2, x3; long y1, y2, y3; cin >> a >> b >> c; cin >> x1 >> x2 >> x3; cin >> y1 >> y2 >> y3; cout << max(a, b, c) << endl; cout << max(x1, x2, x3) << endl; cout << max(y1, y2, y3) << endl; return 0; } int max(int a, int b, int c) { if (b > a)a = b; if (c > a)a = c; return a; } double max(double a, double b, double c) { if (b > a)a = b; if (c > a)a = c; return a; } long max(long a, long b, long c) { if (b > a)a = b; if (c > a)a = c; return a; }
7.5 函数模板
虽然函数的重载可以实现一个函数名多用,但程序中未免还是太复杂,如何进一步简化?
函数模板!!
- 定义函数模板的一般形式:template <typename T>
- 函数模板:建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代替,凡函数体相同的函数均可使用这个模板。(C语言不适用,C++适用)
举例说明:求3个数中的最大数。(分别考虑整数,双精度数,长整数这三种情况),用函数模板来实现
#include<iostream> using namespace std; template<typename T> T max(T a, T b, T c) { if (b > a)a = b; if (c > a)a = c; return a; } int main() { int a, b, c; double x1, x2, x3; long y1, y2, y3; cin >> a >> b >> c; cin >> x1 >> x2 >> x3; cin >> y1 >> y2 >> y3; cout << max(a, b, c) << endl; cout << max(x1, x2, x3) << endl; cout << max(y1, y2, y3) << endl; return 0; }