实验3 函数和数组
这个是我学习C++上机题,你们试试自己完成一下
一、实验目的
- 掌握函数的定义,调用以及函数值的返回。
- 掌握递归函数的定义与调用。
- 掌握内联函数的定义与调用。
- 掌握一维、二维数组的定义与使用。
- 掌握数组在函数参数中的传递方式。
- 掌握一维数组的排序,并用二维数组处理矩阵。
二、知识要点
(一)函数
1. 函数的定义格式
<类型名><函数名>([<参数表>])
<函数体>
- 函数的原型声明
在C++中,如果一个函数要调用另外一个函数,则需要在本函数(称为主调函数)定义之前将要被调用的函数事先声明。函数原型的声明形式是:
<类型><被调用函数名>([<形参表>]);
- 函数的调用
函数调用的格式为:
函数名([<实参表>]);
- 函数的参数
在定义函数时,C++语言用三种方式将函数的形式参数分别声明为值参数、地址参数和引用参数。
A.值参数
传递规则:直接将实参的值复制给形参。这种传递方式的特点是无论被调用函数如何改变形参,都不会对实参产生任何影响。
B.地址参数
传递规则:实参在向形参传递时复制的是实参的地址。这种传递方式的特点是形参的改变会对实参产生影响。
C.引用参数
传递规则:实参在向形参传递时复制的是实参的"别名",这种传递方式的特点是形参的改变会对实参产生影响。
D.在C++中,函数在原型声明或定义时可以在形参表内预先给形参指定默认的值,这样的函数称为带默认值的函数,其形参表的一般形式为:
<类型><形参1>=<表达式1>,......,<类型><形参n>=<表达式n>
- 函数的嵌套调用和递归调用
嵌套调用:在一个函数的执行过程中去调用另外一个函数处理相应任务的方法。
基本原则:被调用的函数必须提前定义,如果被调用的函数定义在主调函数之后,则必须在主调函数前的适当位置有被调函数的函数原型声明。
- 内联函数
在一个函数的定义或声明前加上关键字inline就可以把该函数定义为内联函数。
使用内联函数需要注意以下几点:
(1)内联函数中不能含有任何循环语句以及switch和goto语句。
(2)内联函数的声明必须出现在内联函数第一次被调用之前。
(3)内联函数中不能声明数组。
(4)递归函数不能被定义为内联函数。
- 函数的重载
在C++语言中允许定义多个同名函数,这个功能被称为函数重载。
需要注意的是重载函数的形参在个数或类型上必须不同,不能仅依靠函数的返回值类型来区别重载函数,必须从形参数上区别开来。
- 作用域
在C++中,作用域可以分为5种:函数原型作用域,函数作用域,块作用域,类作用域和文件作用域。
- 全局变量和局部变量
在函数内部或块内部声明的变量称为局部变量,它具有块作用域,即从它声明的那一点开始到这个声明所在的块或函数结束为止。
在一个程序文件中,在所有函数外部定义的变量称为全局变量,有时也称为全程变量、公用变量。全局变量作用域是文件作用域,即从定义变量的位置开始到本程序文件结束。
- 变量的存储类型和生命周期
在C++中,变量的存储类型分为4种:自动类型(auto),寄存器类型(register),外部类型(extern),静态类型(static)。用这4种类型说明的变量分别称为自动变量,寄存器变量,外部变量,静态变量。
生命周期是指一个标识符(变量,函数,类,对象等)从被创建开始到被释放为止的时间。
- 编译预处理
编译预处理是指在对源程序进行正常的编译之前,先对这些命令进行预处理,然后将预处理的结果和源程序一起再进行编译处理。C++提供的编译处理命令有:宏定义命令,文件包含命令和条件编译命令,这些命令均为"#"开头,以区别于语句。
(1)宏定义
不带参数的宏定义:不带参数的宏定义相对比较简单,如下:
#define 宏名 字符串(或数值)
带有参数的宏定义:在宏定义中允许宏名后带参数,如下:
#define 宏名(参数表) 字符串
(2)文件包含
预处理指令#include 称为文件包含指令。
#include 指令有两种书写形式:
第一种:#include <文件名>
第二种:#include "文件名"
(3)条件编译
条件编译指令有如下几种形式:
形式一:
#if 常量表达式1
程序段1
#elif常量表达式2
程序段2
……
#elif常量表达式n
程序段n
#else
程序段n+1
#endif
形式二:
#ifdef 标志符
程序段1
#else
程序段2
#endif
形式三:
#ifndef 标志符
程序段1
#else
程序段2
#endif
(二)数组
数组就是把一系列有序的相同类型的数据组合起来的数据集合,数组都有一个名字即数组名。数组中的每一个数据成为数组的元素。数组元素在数组中的位置由下标确定。根据数组元素下标的个数,数组分为一维数组和多维数组。当数组元素的数据类型为字符型时,该数组称为字符数组。
1.一维数组的定义与初始化赋值
(1)一维数组的定义
在使用数组前,必须对数组进行定义。一维数组的定义包括对数组名、数组元素的数据类型和个数的说明。一维数组的定义格式为:
{存储类型}<类型><数组名>[<常量表达式>];
(2)一维数组的初始化赋值
像普通变量一样,在数组定义的同时就可以对数组元素进行赋值。方法是从数组的第一个元素开始依次给出初值,形成一个初值表,表中各个初值之间用逗号分开,初值表用一对花括号包含起来。一维数组初始化赋值的方法有:
a.给数组的所有元素赋初值。
b.给数组的部分元素赋初值。
c.当把数组定义为全局变量或静态变量时,所有数组元素的初值均为0,当把数组定义为其他存储类型的局部变量时,数组元素没有确定的值,即其值是随机的。
2.一维数组在内存中的存储方式
当定义了一个数组之后,系统就为数组分配一串连续的存储单元,来依次存放各个数组元素。
3.一维数组元素的访问
数组必须先定义后使用。C++规定只能对数组中的元素进行访问,不能把整个数组作为一个整体使用。一维数组元素的访问形式:
<数组名>[<下标表达式>]
下标表达式的值就是被访问的数组元素的下标,其数据类型必须为整型。
4.二维数组的定义与初始化赋值
由两个或两个以上下标的数组元素所组成的数组称为多维数组。下面以二维数组为例子说明多维数组的定义和使用方法。
一维数组中的元素带有一个下标,而二维数组中的元素则带有两个下标。一维数组在逻辑上可以想象成一行长表,而二维数组在逻辑上可以想象成是由若干行,若干列组成的一张表格。
(1)二维数组的定义
{存储类型}<类型><数组名>[<常量表达式1>][<常量表达式2>];
(2)二维数组的初始化赋值
和一维数组一样,二维数组也可以进行初始化赋值。二维数组初始化赋值的方法有:
a.给数组的所有元素赋初值
b.给数组的部分元素赋初值
给数组的部分元素赋初值的方法与给数组的所有元素赋初值的方法类似。
- 二维数组在内存中的存储方式
虽然在逻辑上可以把二维数组看作是一张表格或者一个矩阵,但是在计算机中存储二维数组时,也是在内存中开辟一串连续的存储单元,依次存放各个数组元素。
C++中,是按行为主顺序存放二维数组各个数组元素的,即先存放第一行上的元素,再存放第二行上的元素,依次把各行上的元素存入一串连续的存储单元中。
- 二维数组元素的访问
二维数组元素的访问形式为:
<数组名>[<下标表达式1>][<下标表达式2>]
下标表达式1和下标表达式2的值就是被访问的数组元素的两个下标,其数据类型必须为整数。多维数组的定义和使用方法与二维数组类似。
三、实验内容和步骤
【实例1】从键盘输入两个任意整数,实现值参数传递调用。
#include <iostream>
using namespace std;
void swap(int x,int y)
{
int temp;
temp = x;
x=y;
y=temp;
}
void main()
{
int a,b;
cout<<"please input two integer numbers:"<<endl;
cin>>a>>b;
swap(a,b);
cout<<"after function called"<<"a="<<a<<","<<"b="<<b<<endl;
}
要求:
- 被调用函数定义的位置;
- 被调用函数的调用方法,尤其注意形式参数和实际参数的区别;
- 体会值参数的传递规则;
【实例2】编写程序,计算键盘上输入的某个正整数的阶乘并输出。
#include <iostream>
using namespace std;
long int fac(int n);
void main()
{
int n;
cout<<"input a positive integer:";
cin>>n;
long fa=fac(n);
cout<<n<<"!="<<fa<<endl;
}
long fac(int n)
{
long int q;
if (n==0 || n ==1)
{
q=1;
}
else
{
q=n*fac(n-1);
}
return q;
}
【实例3】某小组有10个学生进行了数学考试,求他们成绩的平均分,最高分和最低分。
题目分析:求N个数的平均值的方法是,求得N个数累加的和之后,并保存在变量SUM中,然后将累加和SUM除以数据个数N,即可求得N个数的平均值。求N个数中最大值的方法是,首先设变量MAX,存放第1个数,然后将余下的数按次序分别与MAX进行比较,若某一个数大于MAX,则将其值赋给MAX,若某一个数小于MAX,则MAX的值不变,当余下的数都比较完后,MAX中存放的就是N个数中的最大的值。求N个数中的最小值的方法与求N个数中的最大值的方法类似,流程图如下:
【实例4】某班有10个学生,进行了数学考试,现要求将数学成绩按由低到高的顺序排序。
分析:排序是指将一组无序的数据按从小到大(升序)或从大到小(降序)的次序重新排列。排序的方法很多,这里介绍一种简单的排序方法——冒泡法。
例如:要求将5个数7、5、6、3、2按从小到大的次序排列。
第一轮第1次将第1个数7和第2个数5进行比较,因7大于5,所以将它们交换,第2次将第2个数7和第3个数6进行比较,因7大于6,所以将它们交换,依次类推共进行5-1=4次,这时,第1轮比较结束,可以看到:最大的数7已“沉底”,即成为最下面的一个数,而小的数“上升”。第2轮第1次将第1个数5和第2个数6进行比较,因5小于6,所以不变,依次类推共进行5-1-1=3次(因第5个数已是最大数,所以不必再将第4个数与第5个数进行比较,所以少1次),这时第2轮比较结束,得到3、5、2、6、7的数据序列。以此类推,第3轮进行2次比较,得到3、2、5、6、7的数据序列。第4轮进行1次比较,得到2、3、5、6、7的数据序列,完成了排序工作。
所以,对5个数进行排序,要进行4轮比较,在第1轮中要进行4次相邻两个数之间的比较,在第2轮中要进行3次相邻两个数之间的比较,在第3轮中要进行2次相邻两个数之间的比较,在第4轮中只进行1次相邻两个数之间的比较,在比较时,如果前面一个数大于后面一个数,则要交换它们的值,否则就不变。这样才能使5个数按从小到大的次序排列。这种排序方法被形象地称为“冒泡法”,在排序的过程中,小的数就象气泡一样逐层上冒,而大的数则逐个下沉。
流程图如下图所示,程序如下:
#include <iostream>
#include <iomanip>
using namespace std;
#define N 10
void main()
{
int a[N],temp;
int i,j;
cout<<"Input score:";
for (i=0;i<=N-1;i++)
cin>>a[i];
for (i=1;i<=N-1;i++)
for (j=0;j<=N-i-1;j++)
if (a[j]>a[j+1])
{
temp=a[j];
a[j] =a[j+1];
a[j+1]=temp;
}
for (i=0;i<=N-1;i++)
cout<<setw(3)<<a[i];
cout<<endl;
}
【实例5】假定在一维数组a[10]中保存着10个整数:42,55,73,28,48,66,30,65,94,72。编写程序,从中顺序查找出具有给定值x的元素,若查找成功则返回该元素的下标位置,否则查找失败返回-1。
题目分析:假定把从一维数组中顺序查找的过程单独用一个函数模块来实现,把调用该函数进行顺序查找通过主函数来实现,则整个程序如下:
#include<iostream >
using namespace std;
const int N=10;
int a[N]={42,55,73,28,48,66,30,65,94,72};
int SequentialSearch(int x)
{
/*补充程序*/
}
void main()
{
int x1=48,x2=60,f;
f=SequentialSearch(x1);
if(f==-1)
cout<<"查找"<<x1<<"失败!"<<endl;
else
cout<<"查找"<<x1<<"成功!"<<"下标为"<<f<<endl;
f=SequentialSearch(x2);
if(f==-1)
cout<<"查找"<<x2<<"失败!"<<endl;
else
cout<<"查找"<<x2<<"成功!"<<"下标为"<<f<<endl;
}
根据题目要求,完成SequentialSearch()函数。
【实例6】将一个二维数组的行和列元素互换,形成另一个二维数组,即数组的转置运算。
例如:
题目分析:将a数组转换成b数组,只要将每个数组元素的两个下标交换即可,即b[j][i]=a[i][j],本程序的主要语句如下:
for(i=0;i<3;i++)
{
for (j = 0;j<4;j++)
{
b[j][i] = a[i][j];
}
}
把该程序补充完整。
程序示例为:
#include <iostream>
#include <iomanip>
using namespace std;
void main()
{
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int b[4][3];
//处理前,添加语句
for (i =0;i<3;i++)
{
for (j = 0;j<4;j++)
{
b[j][i] = a[i][j];
}
}
//处理后,添加语句
}