C/C++ 第八讲 函数 8.1 代码重用和模块化思想 大量代码重复,降低开发效率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> using namespace std;int main () { float a,b,c,d,e,f,g,h,i; cin>>a>>b>>c>>d>>e>>f>>g>>h>>i; float area (float x,float y,float z) { float c,s; c=(x+y+z)/2 ; s=sqrt ((c-x)*(c-y)*(c-z)); return s; } s=area (a,b,c)+area (d,e,f)+area (g,h,i); cout<<s<<endl; print ("pause" ); return 0 ; }
函数研究的3要素:功能、输入值、输出值。
8.2 函数的定义、调用与说明 函数的定义 1 2 3 4 函数类型 函数名(形式参数类型表) { 函数体 }
说明:
函数名需符合标识符的命名规则
形参(形式参数)是函数接受调用者向函数传值的主要途径。
类型 形参名1,类型 形参名2,…,类型 形参名n
float area(float x,floaty,float z)
不能写成:
float area(float x,y,z)
函数体由语句、分程序组成,用来完成函数的功能。
例:
1 2 3 4 5 6 7 8 9 int f1 () { ... int f2 () { ... } }
函数的类型就是函数返回值的类型
例:
1 2 3 4 5 6 7 8 int f (intx) :{ float b=x+3.5 ; return b; } void main () { cout<
### 例:打印多个三角塔
* 使用函数pic
* `pic()`无参数;没有返回值(void型,无类型或空类型)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void pic () { for (int i=0 ;i<5 ;i++) { cout<<setw (10 -i); for (int j=0 ;j<2 *i-1 ;j++) cout<<"*" ; cout<<endl; } } int main () { pic (); pic (); system ("pause" ); return 0 ; }
### 函数调用
形式:
`函数名(实在参数表)`
例:求max
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int getmax (int x,int y) { int z; if (x>y) z=x; else z=y; return z; } void main () { int a,b,c,m,n; cin>>a,b,c; m=getmax (a,b); n=getmax (c,m); cout<<n<<endl; }
说明:
* 实参(实在参数)与函数定义时形参的个数、类型、次序要一一对应。
* 函数调用形式:
* 出线在表达式中:
* 语句:
#### **函数调用与返回过程**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int getmax (int x,int y) { int z; if (x>y) z=x; else z=y; return z; } void main () { ... m=getmax (a,b); ... }
#### **函数的嵌套调用**
例:求正整数m,n的最大公约数和最小公倍数。
1 2 3 4 5 6 7 8 9 10 11 12 int gcd (int m,int n) { while (int r=m%n) {m=n;n=r;} return (n); } int sct (int m,int n) { return (m*n/gcd (m,n);) }
注:
在函数调用过程中,如果函数funA调用了函数funB,函数funB又调用了函数funA,这称为函数的间接递归调用。
#### 函数说明
函数的调用在前,定义在后时,则必须对函数进行说明,形式:
`函数类型 函数名(形式参数类型表);`
注意:
* 函数说明和所定义函数的函数在**返回类型**、**函数名**、**参数类型**和**个数**必须完全一致;
* 函数说明中的参数名可以缺省(只给出形参的个数和类型),也可以与函数定义中的参数名不同。
如:
`int max(int, int)`
==可能考改错题!==
### 例
1. 若有以下函数调用语句:`fun(a+b,(x,y),fun(n+k,d,(a,b)));`在此函数调用语句中实参的个数是:
3个
## 8.3 函数间参数传递
参数传递方式:
* 传值参数
* 传址参数
* 引用参数
### 先导:实参和形参
实参(主调)、形参(被调)
实参有存储空间,形参的的空间在函数被调时分配,可能是传数据过来(得到值,对实参无影响),也可能是传地址(到地址里干活,实参就变了)。
### 传值参数
* 形参是基本数据类型的变量
#### 实参与形参结合过程
* 函数调用时,系统为形参分配新存储单元,并将实参的值赋给相应的形参;
* 通过对形参的操作完成函数体,被调函数的操作都在形参的存储单元中进行,与实参无关;
* 释放形参所占用的存储单元,返回主调函数。
例:交换两个整数的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include "iostream" using namespace std;void swap (int a,int b) { int temp; temp=a;a=b;b=temp; } int main () { int x,y; cout<<"请输入x,y" <<endl; cin>>x>>y; swap (x,y); cout<<"换后x=" <<x<<"y=" <<y<<endl; system ("pause" ); return 0 ; }
**分析:**
`swap()`对形参`a,b`操作,对实参`x,y`无影响。
#### 传值参数传递特点
参数值从实参向形参单向传递,函数中形参值的改变,对实参值无影响。
### 引用参数
#### 引用的概念
* 引用:一种特殊类型的变量,可认为是另一变量的别名。
* 例:`int x=3,&a=x;`
a是x的引用
* 若a是x的引用,**a和x使用同一内存单元,是同一变量的两个名字**,a变则x变,x变则a变。
#### 引用参数作函数参数
* 形参是引用,形参和实参使用相同的内容单元
例:(修改前例)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include "iostream" using namespace std;void swap (int &a,int &b) { int temp; temp=a;a=b;b=temp; } int main () { int x,y; cout<<"请输入x,y" <<endl; cin>>x>>y; swap (x,y); cout<<"换后x=" <<x<<"y=" <<y<<endl; system ("pause" ); return 0 ; }
**分析:**
`swap()`对**形参**`a,b`操作,等价于对**实参**`x,y`操作。
#### 例1
编一函数,判别一个自然数是否是降序数,同时求出该数各位数之和并加以调用,若是降序数输出“ yes”,否则输出“ no”。
例如: 3、 441、 531 是降序数,而 412 不是降序数。
**分析:**
* 函数如何返回两个值给主函数?
* 是否为降序数return返回;
* 各位数上的和借助引用参数。
* 如何判断自然数n是否为降序数?
* 取该数相邻两位比较
1 2 3 4 bool flag=1 ;while (n>=10 &&flag){ if (n/10 %10
``
函数:(修改前例)
1 2 3 4 5 6 7 bool drop (int n,int &sum) { int x=n; bool flag=1 ; while (n>=10 &&flag) { if (n/10 %10
`
1 2 3 4 5 6 int main () { int m,sum1=0 ; cin>>m; if (drop (m,sum1)) cout<
` ``
````` ```` ``
### 形参是指针变量
修改前例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include "iostream" using namespace std;void swap (int *a,int *b) { int temp; temp=a;a=b;b=temp; } int main () { int x,y; cout<<"请输入x,y" <<endl; cin>>x>>y; swap (&x,&y); cout<<"换后x=" <<x<<"y=" <<y<<endl; system ("pause" ); return 0 ; }
#### 指针参数传递特点
* 形参中存放实参内存单元的地址
* 改变形参指向的内存单元的内容就是改变实参的值
* 不能在函数内对指针变量`*x++`等,需要令`p++`,再令`*x=p`。
对比:
1 2 3 4 5 6 7 8 9 10 11 void swap (int *a,int *b) { int temp; temp=*a;*a=*b;*b=temp; } void swap (int *a,int *b) { int *temp; temp=a;a=b;b=temp; }
第一段ok:把实参 a 和 b 变量的地址传递给形参指针 x 和 y, 在被调函数内通过交换 x 和 y 所指向的内存单元的内容间接交换了 a 和b 的值 。
第二段no:只有对形参指针变量所指的内容进行修改,对应的实参值才会随之改变。
> 要看交换的是什么,是形参的值(实参值跟着变)还是形参本身(地址换一换)?
#### 例2
随机生成10个1~100数放在一维数组中,求其平均值及最大元素值,并在主函数中显示结果。
**分析:**
* 如何把数组传递给函数用于计算
* 传递数组的首地址,对应的形参为指针变量
* 如何传回二值
* 指针参数/引用参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void fun (float *p,float *p1,float *p2) { float sum,max; sum=max=*p++; for (int i=0 ;i<N;i++) { if (max>*p) max=*p; sum+=*p; p++; } *p1=sum/N; *p2=max; } int main () { float a[10 ],aver,max1,x; for (int i=0 ;i<N;i++) { srand (time (NULL )); x=rand ()%100 +1 ; a[i]=x; } fun (a,&aver,&max1); cout<<aver<<max1<<endl; system ("pause" ); return 0 ; }
* 无法传回多个值的解决方法:如上例,用void函数带指针变量&aver等,在函数内修改形参指向的内容。
### 指针参数作实参
e.g.
1 2 3 4 5 6 7 8 9 10 11 12 13 int main () { char *a,*b; b=new char [20 ]; a=new char [20 ]; cin>>a>>b; copy_string (a,b); cout<<a<< b<<endl; delete []a; delete []b; system ("pause" ); return 0 ; }
### 例:打印万年历
**自顶向下逐步求精**的设计思想
* 年历分解为月历
* 月历=月头+1日打印位置+月总天数+周六换行
* 1日打印位置
* 闰年判别
* 月总天数
* 计算每月1日与1900.1.1间差距天数。`d%7=0~6`,分别代表周一~周日

方法:从问题的总体目标开始,忽略底层细节,构造底层结构,再不断分解、细化。
## 8.4 数组名作函数参数
形参为数组名,实参可以是**数组名**或指针变量。
数组作为函数的参数时,系统为形参数组和实参数组分配相同的存储单元。
> 一维数组作参数传递时,只用数组名。
>
> 数组长度用#define N 10或者const N=10解决
### 例
#### 1
编写函数 sort,对包含 n 个元素的整型数组 x 进行递减排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void sort (int a[],int n) { int i,j,k,w; for (i=0 ;i<n-1 ;i++) { k=i; for (j=i+1 ;j<n;j++) if (a[k]<a[j]) k=j; if (i!=k) { w=a[i]; a[i]=a[k]; a[k]=w; } } }
注:
* 形参写法:
* `类型 形参数组名[]`
`void sort(int a[])`
* 数值型数组,数组中元素数n一般作参数,传给形参
**调用**
* 实参写法:
* `实参数组名`
`sort(a,10)`
#### 2
数字:4bytes,256~65536
字符串:n+1个字节
## 8.5 参数缺省
默认值参数必须从最右边开始,不能跳过最后一个让倒二取默认值。
**1、**要求赋予默认值的参数必须放在形参表列中的最右端。如:
void fun(int i, int j, int k, int m=3,int n=4);
**2**、若参数在函数说明中已设置缺省值,则在函数定义中不能重复设置
**3**、实参个数至少要等于没有提供默认值的形参个数
**假设函数调用语句为:**
**fun(1,2);** //错误,至少应有三个实参
**fun(10,20,30);** //正确,m、n取默认值
**fun(10,20,30,40);** //正确,m取40、n取默认值4
**fun(10,20,30, ,50);** //错误,只能从左至右匹配
## 8.6 函数重载
允许函数同名。
* **参数类型不同的重载函数**
e.g. int, long, float类型各自的abs()
* **参数个数不同的重载函数**
e.g. 2,3,4个数的min()
## 作业题
### 1
编一判断m是否为素数的函数,并在主函数中利用它输出十对最小的孪生素数。所谓孪生素数是指两个相差为2的素数,如3和5,11和13。程序运行结果见下图。
函数形式为:
`bool isprime(int m)`
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include "iostream" using namespace std;bool isprime (int m) { int i; bool flag = 0 ; for (i = 2 ; ; i++) if (m % i == 0 ) break ; if (i == m) flag= 1 ; return flag; } int main () { int m=2 , s = 0 ; while (s < 10 ) { if (isprime (m) && isprime (m + 2 )) { cout << "<" << m << "," << m + 2 << ">" << endl; s++; } m++; } system ("pause" ); return 0 ; }
### 2
编一函数,功能为判断一字符串是否为回文,如果是回文则返回1,否则返回0。回文是指顺读和倒读都一样的字符串,如“deed”和“level”是回文。在主函数中对输入的字符串加以调用。
函数形式为:`int huiwen(char s[])`
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include "iostream" #define N 100 using namespace std;int huiwen (char s[]) { int i,m; m = strlen (s); bool flag = 1 ; for (i = 0 ; i <= (m - 1 ) / 2 ; i++) { if (s[i] != s[m - 1 - i]) { flag = 0 ; } } return flag; } int main () { char s[N]; gets_s (s); int flag; flag = huiwen (s); cout << flag << endl; system ("pause" ); return 0 ; }
### 3
函数的功能是将学生成绩从高分到低分排序,并统计优秀与不及格的人数。用下面两种方法实现:
(1)函数形式为:int fun1(int s[],int n,int *x)
要求优秀人数通过return返回,不及格人数通过指针参数返回结果。
(2)函数形式为:void fun2(int s[],int n,int &x,int &y)
要求优秀与不及格的人数通过引用参数返回结果。
分别编二个函数,学生人数从键盘输入。
函数一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include "iostream" #define N 100 using namespace std;int fun1 (int s[], int n, int *x) { int i,j,k,t,r=0 ,p=0 ; for (i = 0 ; i < n-1 ; i++) { k = i; for (j = i + 1 ; j < n; j++) if (s[k] < s[j]) k = j; if (i != k) { t = s[i]; s[i] = s[k]; s[k] = t; } } for (i = 0 ; i < n; i++) { if (s[i] >= 90 ) r++; if (s[i] < 60 ) p++; } *x = p; return r; } int main () { int s[N]; cout << "输入学生人数:" ; int n,i, num=0 ; cin >> n; cout << "输入学生成绩(满分100):" ; for (i = 0 ; i < n; i++) { cin >> s[i]; } fun1 (s, n, &num); cout << "优秀人数为" << fun1 (s, n, &num) << endl; cout << "不及格人数为" << num << endl; cout << "从高到低排序成绩:" << endl; for (i = 0 ; i < n; i++) { cout << s[i]<<' ' ; } system ("pause" ); return 0 ; }
函数二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include "iostream" #define N 100 using namespace std;void fun2 (int s[], int n, int & x, int & y) { int i, j, k, t, r = 0 , p = 0 ; for (i = 0 ; i < n - 1 ; i++) { k = i; for (j = i + 1 ; j < n; j++) if (s[k] < s[j]) k = j; if (i != k) { t = s[i]; s[i] = s[k]; s[k] = t; } } for (i = 0 ; i < n; i++) { if (s[i] >= 90 ) r++; if (s[i] < 60 ) p++; } x = r; y = p; } int main () { int s[N]; cout << "输入学生人数:" ; int n,i, x, y; cin >> n; cout << "输入学生成绩(满分100):" ; for (i = 0 ; i < n; i++) { cin >> s[i]; } fun2 (s, n, x,y); cout << "优秀人数为" << x << endl; cout << "不及格人数为" << y << endl; cout << "从高到低排序成绩:" << endl; for (i = 0 ; i < n; i++) { cout << s[i]<<' ' ; } system ("pause" ); return 0 ; }
### 4
编一函数,功能为统计字符串中各个字母(不区分大、小写)出现的频率,同时找出频率出现最高的字母及次数,假设出现次数最多的字母只有一个。函数形式为:
void freq(char s[],int p[],char &chmax,int &max)
程序运行结果如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include "iostream" #define N 256 using namespace std;void freq (char s[], int p[], char & chmax, int & max) { char *a = s; while (*a) { if (*a > 'a' && *a < 'z' *a>'A' && *a < 'Z' ) { if (*a < 'a' ) p[*a + 32 ]++; else p[*a]++; if (p[*a] > max) { max = p[*a]; chmax = *a; } } a++; } } int main () { char s[N] = "" ; char chmax ; int p[N]={}, max=0 ; gets_s (s); freq (s, p, chmax, max); for (int i = 0 ; i < N; i++) { if (p[i]) cout << char (i) << "----" << p[i] << endl; } cout << "出现频率最高的字母:" << chmax << "----" << max << endl; system ("pause" ); return 0 ; }
`` ```` ````` ``````