以下为个人学习笔记和习题整理
课程:计算机程序设计(C++)- 西安交通大学 @ 中国大学 MOOC
https://www.icourse163.org/course/XJTU-46006
# 课堂笔记
# 模块化程序设计
编写一个规模较大的程序,可按其功能划分为若干相对独立的模块。
好处:
- 程序开发更易控制
- 利于软件重用
- 避免重复代码
- 容易调试和维护
每个模块由一个函数实现。
# 主函数 main()
- C++ 程序仅由一个主函数构成。
- 可以由多个子函数组成
- 程序的执行顺序:总是从 main 函数开始执行,其中可调用其他子函数,最后在主函数中结束。
# 库函数(标准函数)
由系统提供,可以直接使用,但需包含相应的头文件。如:
#include <iostream> // 标准输入 / 输出函数 | |
#include <cmath> // 数学库函数 | |
#include <cstring> // 字符串处理函数 |
# 用户定义函数
程序的主要逻辑
# 函数的定义
<函数值类型> <函数名>(<形式参数表 >) // 函数头 | |
{ // 函数体 | |
<语句序列> | |
} |
# 函数值类型
即函数的返回值类型
- 返回简单类型,如:
int
、long
、float
、double
、char
等 - 返回结构类型
- 返回指针类型
- 返回引用类型
如果函数无任何返回值,这时函数的返回值类型应标记为
void
。void
类型称为无类型或空类型。
# 形式参数表
即形参表,函数的参数表用于实现函数间的数据联系。
- 说明格式:
<类型1> <形参名1>, <类型> <形参名2>, ... , <类型n> <形参名n>
每个形参必须同时给出形参的类型和名称。
如:int max(int a,b)
,作为函数首部定义是错误的。
因为形参 b 缺少类型说明符,即使与 a 类型相同,但作为形参说明也不能省略类型符int
。
定义函数时需考虑设置形参,形参的个数及类型根据需要设定。
也可以没有参数,称为无参函数。形参可以接收主调函数传递的实参的值。
在函数中对形参进行处理,并将处理结果返回到主调函数。形参是在函数调用时分配存储空间,同时接收实参的值。
当函数执行结束后,系统将自动释放形参所分配的存储空间。
因此,形参属于函数的局部变量,只能在该函数中使用。当形参为引用类型或指针类型时,利用形参不仅可以访问实参的值,还可以改变实参的值。
# 函数体
由 { }
括起来的语句序列构成,是实现函数功能的主体。
函数的编写过程类似于主函数。
在函数中可以调用其它函数。
在函数体中,使用
return
语句返回函数执行的结果。
return <表达式>; // 其中表达式类型应与函数返回值类型一致 |
- 对于无返回值的函数,也可使用
return;
将控制返回到主调函数。
在一个函数中允许出现多个
return
语句,但在函数执行期间只能有一个语句起作用。
在函数体的最后位置,一个无返回值的return;
语句可以省略。
# 函数的声明
在 C++ 中,程序编译的单位是源程序文件(即源文件),一个由多个函数构成的程序可以组织存放在一个或多个源文件中。
在源文件中,函数之间的排列顺序没有固定的要求,但要满足 “先定义后使用” 的原则。
对于标准库函数的使用,在程序开头使用
#include
命令将所需的头文件包含进来即可。对于自定义的函数,要么在调用之前定义,要么在调用之前作函数声明。
函数的声明是指在函数被调用之前,对函数的类型、名称以及参数等信息所作的说明。
格式如下:<类型名> <函数名>(<类型1> <形参1>, <类型2> <形参2>,...);
// 或
<类型名> <函数名>(<类型1>, <类型2>, ...); // 可省略形参
在形式上就是在函数定义的首部后加分号
;
构成。
函数声明说明了函数所采用的形式,称为函数原型。
#include <iostream> | |
#include <cmath> | |
using namespace std; | |
int main() | |
{ | |
double Area(double a, double b, double c); | |
// 或者 | |
// double Area(double, double, double); | |
double a,b,c; | |
cout<<"请输入三个边长:"<<endl; | |
cin>>a>>b>>c; | |
cout<<"面积为:"<<Area(a,b,c)<<endl; | |
return 0; | |
} | |
double Area(double a, double b, double c) | |
{ | |
double p,s; | |
p=(a+b+c)/2; | |
s=sqrt(p*(p-a)*(p-b)*(p-c)); | |
return s; // 返回函数的值 | |
} |
# 函数的调用
函数定义后,并不能自动执行,必须通过函数调用来实现函数的功能。
函数调用,即控制执行某个函数。
C++ 中,主函数可以调用其它子函数,而其它函数之间也可以相互调用。
# 一般格式
<函数名>(<实际参数表>) // 有参调用 | |
// 或 | |
<函数名>() // 无参调用 |
<函数名>
为要使用的函数的名字。<实际参数表>
是以逗号分隔的实参列表,必须放在一对圆括号中。<实参表>
与<形参表>
中参数的个数、类型和次序应保持一致。- 当调用无参函数时,函数名后的圆括号不能省略。
# 实参的几种形式
形参为简单类型变量,对应的实参可以是:常量,变量及表达式。
形参为数组,对应的实参为数组(名)。
形参为结构类型,对应的实参为结构类型变量。
调用已知三边求三角形面积的函数 Area。
double Area(double,double,double); // 函数声明 | |
cout<<Area(4.0,5.0,6.0)<<endl; // 常量作实参 | |
cout<<Area(a,b,c)<<endl; // 变量作实参 | |
cout<<Area(a+1,b+1,c+2)<<endl; // 表达式作实参 |
函数调用出现在表达式中(适于有返回值的函数调用形式)
如,函数 max()
求两个数的最大值。函数原型如下:
float max(float x,float y); |
该函数有返回值,调用时应出现在表达式中。
c=max(a,b); // 函数调用出现在赋值运算符右边的表达式中 | |
d=max(c,max(a,b));// 函数调用同时出现在实参表达式中 | |
cout<<max(a,b)<<endl;// 输出一个函数值 |
# 嵌套调用
函数的嵌套调用是指在调用一个函数的过程中,被调用的函数又调用了另一个函数。
# 参数的传递
三种方式:
# 值传递
调用时,将实参的值传递给对应的形参,这就是值传递。
在值传递过程中,改变形参的值,并不会改变实参的值。
好处:
减少函数之间的数据依赖,增强了函数自身的独立性。注意:
函数的形参声明为简单类型或结构类型变量,实参与形参将采用值方式传递。
void swap(int x, int y) | |
{ | |
int tmp; | |
tmp = x; | |
x = y; | |
y = tmp; | |
} | |
int main() | |
{ | |
int a = 1, b = 2; | |
cout << "Before exchange:a= " << a << ",b= " << b << endl; | |
// a=1, b=2 | |
swap(a, b); // 独立语句调用 | |
cout << "After exchange:a= " << a << ",b= " << b << endl; | |
// a=1, b=2 | |
return 0; | |
} |
调用前实参 a,b 有自己的存储空间,并有初值。
调用函数时,为形参 x,y 分配存储空间,并接收实参的值。
返回到主函数,这时实参 a 和 b 的值没有改变。
函数中,是对 x 与 y 值进行交换。
执行结束后,x 与 y 将自动释放。
# 引用传递
引用是一种特殊的变量,它被认为是一个变量的别名。
# 引用的定义
<数据类型> &<引用名>=<目标变量名>; |
&
为引用(变量)的标志符号, <引用名>
是一个标识符。<数据类型>
为 <目标变量>
的类型。
int a,&b=a; | |
//a 是一个整型变量,b 是一个引用整型变量 a 的引用, | |
// 即 b 是 a 变量的一个别名。 | |
// 这时,使用 a 与使用 b 是等价的。 |
# 使用说明
- 定义一个引用,其实是为目标变量起一个别名。
- 引用并不分配独立的内存空间,它与目标变量共用其内存空间。
- 定义一个引用(变量)时,如果该引用不是用作函数的参数或返回值,则必须提供该引用的初值(即必须提供引用的目标变量名)。
- 使用引用与使用目标变量效果是相同的。
int main() | |
{ | |
int a=2,&b=a; | |
cout<<&a<<" "<<&b<<endl;// 输出变量的地址 | |
cout<<a<<" "<<b<<endl; // 输出变量的值 | |
return 0; | |
} |
# 引用的传递
为实现引用传递,这时函数的形参应定义为引用类型变量,而对应的实参应为变量名,该变量将作为引用的目标变量名。
函数调用时,作为形参的引用变量并不分配新的内存空间,它将作为实参变量的别名与其共用内存。
使用引用参数可以直接操作实参变量,从而能够实现通过修改形参的值而达到修改对应实参值的目的。
通过设置多个引用参数,可以从函数中带回多个结果值。
注意:引用作为函数形参,其引用的目标变量默认为调用该函数时对应的实参变量名,所以,在定义函数时,对于引用类型参数不必提供引用的初值。
//x,y 为引用参数,x 引用 a,y 引用 b | |
void swap(int &x, int &y) | |
{ | |
int tmp; | |
tmp = x; | |
x = y; | |
y = tmp; | |
// 交换 x,y 就是交换 a,b | |
} | |
int main() | |
{ | |
int a = 1, b = 2; | |
cout << "Before exchange:a= " << a << ",b= " << b << endl; | |
// a=1, b=2 | |
swap(a, b); // 独立语句调用 | |
cout << "After exchange:a= " << a << ",b= " << b << endl; | |
// a=2, b=1 | |
return 0; | |
} |
# 指针传递
# 为形参指定默认值
C++ 语言允许在函数定义时为形参指定默认参数值。这样,在函数调用时,如果没有提供实参,则形参自动使用其默认值。
void f(int x=0,int y=0); // 形参的默认值为 0 | |
f();// 未提供实参值,形参 x,y 使用默认值 0 | |
f(2,4);// 形参 x,y 将使用实参的值 2 和 4 | |
f(1);// 形参 x 接收 1,而 y 使用 0 |
可以对部分形参定义默认值。
这时默认值应出现在从右到左的连续若干个形参中。
void f(int i,int j=2,int k=3);// 函数原型正确 | |
void f(int i,int j,int k=3); // 函数原型正确 | |
void f(int i=1,int j,int k=3);// 函数原型错误 | |
void f(int i=1,int j,int k);// 函数原型错误 |
指定参数的默认值可以在函数定义中进行,也可以在函数原型中进行。
通常写在函数名在程序中第一次出现的位置。
# 数组作为函数参数
# 一维数组的传递
<类型> <数组名>[] |
其中, []
中可以省略数组的长度值。(可认为形参数组与实参数组长度相同)
对应的实参应为同类型的一维数组名。(仅用数组名)
为了使函数知道需要处理的数组元素的个数,通常给函数再传递一个表示元素个数的整型数。
# 例子:元素之和
一维数组名作为函数的参数。编写函数,计算一个整型数组中从第 m 个元素(m 从 0 开始)开始的 n 个元素之和。
函数设计:
函数原型:int fun(int b[],int m,int n);
功能:计算数组 b 中从第 m 个元素开始的 n 个元素之和。主函数设计:
定义并初始化一个整型数组 a。
测试 1:fun(a,0,10);//求从第0个元素开始的10个元素之和
测试 2:fun(a,3,5); //求从第3个元素开始的5个元素之和
int fun(int b[],int m,int n) | |
{ | |
int i,s=0; | |
for(i=m;i<m+n;i++) | |
s=s+b[i]; | |
return s; | |
} | |
int main() | |
{ | |
int x,a[]={0,1,2,3,4,5,6,7,8,9}; | |
x=fun(a,0,10); | |
cout<<x<<endl; | |
x=fun(a,3,5); | |
cout<<x<<endl; | |
return 0; | |
} |
# 例子:元素排序
编写一个函数 sort,对 n 个元素的一维整型数组 b 进行从小到大排序。
void sort(int b[],int n) ;//b 为一维数组,n 为元素的个数 | |
{ | |
// 选用一种排序方法对 n 个元素的 b 数组排序(代码略) | |
// 对 b 数组排序,实际就是对实参数组排序 | |
} |
sort(a,10); | |
// 调用 sort 函数,对 10 个元素的整型数组 a 进行排序 | |
// 函数调用后,并输出排序结果,即输出 a 数组中各个元素值。 |
# 二维数组的传递
<类型> <数组名>[][<列数>] |
其中, <列数>
为常数,不能省略,行数可缺省。
调用时的实参应为同类型的二维数组名。
且用作实参的二维数组,其列数必须与形参中的 <列数>
相同。
# 例子:矩阵转置
二维数组名作参数。编写一个函数,将 N 阶方阵转置(N<10)
- 函数设计
void transmat(int a[][10],int n); | |
// 对 a 数组中前 n 行 n 列矩阵元素转置 |
- 主函数设计
定义一个 10 行 10 列的大数组 x,元素类型为整型;
键盘输入一个整数 n (n<10);
键盘输入一个 n×n 的方阵数据,并存放到 x 数组的前 n 行 n 列元素中。
函数调用语句为:transmat(x,n);
按 n 行 n 列输出转置矩阵。
void tranmat (int a[][10],int n) | |
{ | |
int t; | |
for(int i=0;i<n-1;i++) { | |
for(int j=i;j<n;j++) | |
{ | |
t=a[i][j]; | |
a[i][j]=a[j][i]; | |
a[j][i]=t; | |
} | |
} | |
} | |
int main() { | |
int x[10][10],n; | |
cout<<"输入n(n<10):"; | |
cin>>n; | |
cout<<"输入"<<n<<"行"<<n<<"列元素:"<<endl; | |
for(int i=0;i<n;i++) { | |
for(int j=0;j<n;j++) { | |
cin>>x[i][j]; | |
} | |
} | |
tranmat(x,n); | |
cout<<"转置矩阵结果如下:"<<endl; | |
for(int i=0;i<n;i++) { | |
for(int j=0;j<n;j++) { | |
cout<<x[i][j]<<" "; | |
} | |
cout<<endl; | |
} | |
} |
# 结构体变量作函数参数
实参为结构体类型变量
形参为同类型的结构变量
传递方式为值传递
在函数调用时,系统首先为形参变量(即结构变量)分配存储空间,接着将实参变量(结构变量)各成员值传递给形参变量的各个成员。对形参成员的修改不会影响实参成员值的变化。
# 例子:输出结构变量成员
定义一个结构体类型: | |
struct student | |
{ | |
int stno; | |
char name[20]; | |
int age; | |
}; | |
void print(student s) // 输出 | |
{ | |
cout<<s.stno<<endl; | |
cout<<s.name<<endl; | |
cout<<s.age<<endl; | |
} | |
// 主函数 | |
int main() | |
{ | |
student stu={1001, "Li" ,19}; | |
print(stu); // 实参为结构变量 | |
return 0; | |
} |
# 课堂讨论
- 什么时候会使用没有参数或没有返回值的函数?
只起到处理的作用(输入、输出、赋值等)时,没有参数或没有返回值,用来起过渡、衔接等作用,或者空函数留待未来完善。
- 什么时候需要函数的声明,函数的声明和定义有什么区别?
声明是不开辟内存的,仅仅告诉编译器,要声明的部分存在,要预留一点空间。定义则需要开辟内存。
- 函数的定义是一个完整的函数单元:
包含函数类型、函数名、形参及形参类型、函数体等。
在程序中,函数的定义只能有一次。
函数首部与花括号间不加分号。- 函数声明只是对编译系统的一个说明:
函数声明是对定义的函数的返回值的类型说明,以通知系统在本函数中所调用的函数是什么类型。
不包含函数体(或形参)。
调用几次该函数就应在各个主调函数中做相应声明。
函数声明是一个说明语句,必须以分号结束。
- 什么样的函数只能单独作为语句调用,不能放在表达式中?
无返回值函数。
- 什么时候需要引用传递?
需要返回多个结果;节省内存,提高运行速度;改变实参的值时。
- 为什么带默认值的参数要放在不带默认值的参数右边(不能交叉)。
因为在函数体内被传递的实参按从左向右依次初始化,如果带默认值的参数放在不带默认值的参数左边,传递少了或不传递则右边的形参没有被初始化,这是错误的,也就会失去了默认值的可不传递参数特性,所以带默认值的参数要放在不带默认值的参数右边。
- 形参是二维数组时,为什么列的大小不能省略?
数组是按行线性存入数据的,指定列号,相当于数组的行数和列数同时确定,如果不告诉编译器列号,会导致数据存储混乱,这和定义多维数组,一维长度可以没有,但是其他维长度必须有,道理是一样的。
- 如果想传递一个一维结构体数组给函数,形参和实参应是什么样的?
一维数组作为形参的声明格式:
<函数类型> <函数名>(结构体类型 形参数组名[])
其中,[]
中可以省略数组的长度值。(可以认为形参数组与实参数组长度相同。) 仅用数名时,对应的实参应为同一类型的一维数组名。
实参为<函数名>(结构体数组名)
# 随堂练习
关于 C++ 函数的说法哪个是不正确的?
关于函数的定义不正确的是哪项?
关于用户自定义函数的使用,下列哪个说法是正确的?
关于函数的调用,下列哪项说法是正确的?
关于函数调用的值传递,下列哪项是正确的?
关于函数调用的引用传递,下列哪项是正确的?
C++ 中关于有默认值的形参,正确的描述是 。
主函数中有声明语句
int a[100], n=5;
下列哪组实参和形参的对应是正确的?
有结构体 STU 如下:
struct STU{
char name[20];
char number[10];
int score;
};
stu 是 STU 类型的变量。
下列实参和形参的结合哪项是正确的?C