第七章 函数-C++的编程模块
7.1. 复习函数的基本知识
- 提供函数定义
- 提供函数原型
- 调用函数
#include <iostream>
using namespace std;
void simple();
int main(){
simple();
return 0;
}
void simple(){
cout << "hello world" << endl;
}
函数是如何来进行返回值的呢?
函数通过将返回值复制到指定的CPU寄存器或者内存单元中。调用程序将会查看该内存单元。这也就是在这种强类型语言当中,函数也需要声明类型的原因之一。
7.1.2. 函数原型和函数调用
1.为什么需要原型
原型描述了函数到编译器的接口,也就是说,它将函数返回值的类型以及参数的类型和数量告诉编译器。
2.原型的语法
函数原型是一条语句,因此必须以分号结束。使用原型最简单的方法是,复制函数定义中的函数头,然后在后面添加上分号。当然,在参数列表当中,函数类型不要求提供变量名,但是加上的话,程序的可读性会更好。
3.原型的功能
原型用来确保以下几点:
- 编译器正确处理函数返回值
- 编译器检查使用的参数数目是否正确
- 编译器检查使用的参数类型是否正确。
7.2. 函数参数和按值传递
double cube(double x);
double volume = cube(side);
被调用时, 该函数将创建一个新的名为x 的double 变量, 并将其初始化为5。这样, cube( )执行的操作将不会影响main( )中的数据,因为cube( )使用的是side 的副本,而不是原来的数据。稍后将介绍一个实现这种保护的例子。用千接收传递值的变量被称为形参。传递给函数的值被称为实参。出千简化的目的,C++标准使用参数(argument) 来表示实参, 使用参量(parameter) 来表示形参,因此参数传递将参量赋给参数。
在函数中声明的变量(包括参数)是该函数私有的。在函数被调用时,计算机将为这些变量分配内存;在函数结束时,计算机将释放这些变量使用的内存(有些C++文献将分配和释放内存称为创建和毁坏变量,这样似乎更激动人心)。这样的变量被称为局部变量, 因为它们被限制在函数中。前面提到过,这样做有助千确保数据的完整性。这还意味着, 如果在main()中声明了一个名为x的变量, 同时在另一个函数中也声明了一个名为x的变量, 则它们将是两个完全不同的、毫无关系的变量, 这与加利福尼亚州的Albany与纽约的Albany是两个完全不同的地方是一样的道理(参见图7.3)。这样的变量也被称为自动变量, 因为它们是在程序执行过程中自动被分配和释放的。
7.3. 函数和数组
int sum_arr(int arr[], int n);
这里的arr是一个指针。因此,如果需要调用这个函数的话,直接传入一个数组的名字就行了。
7.3.1. 函数如何使用指针来处理数组
int sum_arr(int* arr, int n);
因为数组名这个指针指向的是数组的第一个元素,所以这样定义函数也可以。
arr[i] == *(arr + i);
&arr[i] == arr + i;
7.3.2. 将数组作为参数意味着什么
数组名与指针对应是好事吗?确实是一件好事。将数组地址作为参数可以节省复制整个数组所需的时间和内存。如果数组很大, 则使用拷贝的系统开销将非常大;程序不仅需要更多的计算机内存, 还需要花费时间来复制大块的数据。另一方面, 使用原始数据增加了破坏数据的风险。在经典的C 语言中,这确实是一个问题,但ANSIC 和C++中的const 限定符提供了解决这种问题的办法。
7.3.3. 更多数组函数示例
创建显示数组内容的函数很简单。只需将数组名和填充的元素数目传递给函数, 然后该函数使用循环来显示每个元素。然而, 还有另一个问题一一确保显示函数不修改原始数组。除非函数的目的就是修改传递给它的数据, 否则应避免发生这种情况。使用普通参数时, 这种保护将自动实现, 这是由于C++按值传递数据, 而且函数使用数据的副本。为防止函数无意中修改数组的内容,可在声明形参时使用关键字const。
数组处理函数的常用编写方式
void f_modify(double arr[], int n);
void f_no_change(const double arr[], int n);
7.3.4. 使用数组区间的函数
传两个指针,一个指向数组头,一个指向数组尾。
arr[20]
arr和arr + 20定义了区间
arr + 20是结尾后面的一个位置。
7.3.5. 指针和const
可以用两种不同的方式将const关键字用于指针。
第一种方法是让指针指向一个常量对象这样可以防止使用该指针来修改所指向的值,第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。
7.4. 函数和二维数组
为了编写将二维数组作为参数的函数,必须牢记,数组名被视为它的地址。因此,相应的形参是一个指针。
int data[3][4] = {{1, 2, 3, 4}, {9, 8, 7, 6}, {2, 4, 6, 8}};
int total = sum(data, 3);
data是一个数组名,该数组有3个元素。第一个元素本身就是一个数组,由4个int值组成。因此data的类型是指向由4个int组成的数组的指针。也就是将data[3]作为参数传到函数当中。
那么sum的原型是:
int sum(int (*ar2)[4], int size);
其中的括号是必不可少的,因为下面的声明将声明一个由4个指向int的指针组成的数组,而不是由一个指向4个int组成的数组的指针。另外,函数参数不能是数组。
int *ar2[4]
还有另外一种形式
int sum(int ar2[][4], int size);
上面的两个原型都指出,ar2是指针而不是数组。还需要注意的是,指针类型指出,它指向由4个int组成的数组。因此,指针类型指定了列数,这就是没有将列数作为独立的函数参数进行传递的原因。
7.5. 函数和C风格字符串
7.5.1. 将C风格字符串作为参数的函数
如果要将字符串作为参数传递给函数,则表示字符串的方式有三种:
- char数组
- 用引号括起来的字符串常量
- 被设置为字符串的地址的char指针
但上述3种类型都是char指针,因此可以将其作为字符串处理函数的参数。
将字符串作为参数来传递,实际上传递的是字符串第一个字符的地址。这意味着字符串函数原型应将其表示字符串的形参声明为char *类型。
7.5.2. 返回C风格字符串的函数
char * buildstr(char c, int n);
7.6. 函数和结构
与数组不同,结构将其数组组合成为单个实体或者数据对象,该实体被视为一个整体。
struct travel_time{
int hours;
int mins;
};
travel_time sum(travel_time t1, travel_time t2);
7.6.3. 传递结构的地址
调用函数时,将结构的地址(&travel_time)传递给它
将形参声明为指向travel_time的指针,也就是travel_time *类型。
由于形参是指针而不是结构,因此应使用间接成员运算符(->),而不是成员运算符(.)
7.7. 函数和string对象
string list[SIZE];
void display(const string sa[], int n){
...
}
display(list, SIZE);
7.8. 函数和array对象
const int Seasons = 4;
const array<double, Seasons> Snames = {"Spring", "Summer", "Fall", "Winter"};
void fill(array<double, Seasons> *pa){
cin >> (*pa)[i];
}
array<double, Seasons> expenses;
fill(&expenses);
7.9. 递归
不多说,学数据结构
7.10. 函数指针
函数也有地址。函数的地址是存储其机器语言代码的内存的开始地址。可以编写将另一个函数的地址作为参数的函数。这样的话,第一个函数就能够找到第二个函数并运行。这和直接调用另一个函数相比,很笨拙,但是它允许在不同的时间传递不同函数的地址,这意味着可以在不同的时间使用不同的函数。
- 获取函数的地址
获取函数的地址很简单,和数组一样,直接使用函数名就行。
- 声明函数指针
double pam(int); // 原型
double (*pf)(int); // pf points to a function that takes one int argument and that returns type double
这个是将pam替换为了(*pf)。
由于pam是函数,因此(*pf)也是函数。
如果(*pf)是函数,则pf是函数指针。
如果要将即将要编写的代码行数和估算算法的地址传递给它,那么原型如下:
void estimate(int lines, double (*pf)(int));
上述声明指出,第二个参数是一个函数指针,它指向的函数接受一个int参数,并返回一个double值。
调用的时候,只需要将pam()的地址传递给它:
estimate(50, pam);
使用指针来调用函数
double pam(int);
double (*pf)(int);
pf = pam;
double x = pam(4);
double y = (*pf)(5);
double y = pf(5);