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()
    {
    ...
    }
    }
  • 函数的类型就是函数返回值的类型

    • 函数通过return语句返回一个值给调用它的函数

    • 函数返回值和函数类型最好一致。若不一致,该值转换为函数的类型。

    例:

    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;
}
//return;
}
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
m=getmax(a,b);
//有返回值;
* 语句:
1
2
pic();
//无返回值
#### **函数调用与返回过程**
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);
//实参a,b传入形参x,y
...
}
#### **函数的嵌套调用** 例:求正整数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;
}
//输出的x,y并没有交换,怎么会是呢?
**分析:** `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;//假定x是降序数
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)) //flag==1
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;
} //将形参a,b内存的地址内的东西交换,实参也交换。
//or:
void swap(int *a,int *b)
{
int *temp;
temp=a;a=b;b=temp;
} //交换的是形参a,b(指针)内的值,从a里存x的值b里存y的值,变成a里存y的值b里存x的值
第一段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;
}
//main调用
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`,分别代表周一~周日 ![image-20231026105258695](img/image-20231026105258695.png) 方法:从问题的总体目标开始,忽略底层细节,构造底层结构,再不断分解、细化。 ## 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)//元素个数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) 程序运行结果如下: ![image-20231027162507116](img/image-20231027162507116.png)
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;
}
`` ```` ````` ``````