7.6 函数和结构
对于结构体,可以直接将其赋值给另一个结构体,同样,也可以按值传递,此时,函数将使用原始结构的副本。使用结构编程时,可通过像处理基本类型那样处理结构,既可作为参数传递,又可以作为返回值使用。
函数可以返回结构。与数组不同,数组名表示数组的地址,而结构名只是结构的名称,&结构
才表示该结构的地址(&既用来表示地址运算符怒,还用来表示引用变量,见后续第8章)。
7.6.1 传递和返回结构
另一个处理结构的函数示例:
struct polar
{
double distance;
double angle;
};
//函数声明和定义:
void show_polar(polar pda);
void show_polar(polar pda)
{
cout << “distance is ” << pda.distance << endl;
cout << “angle is ” << pda.angle << endl;
}
//函数调用:
polar pplace;
… //给pplace赋值,
show_polar(pplace);
7.6.3 传递结构的地址
使用指向结构的指针来传递结构的地址而非整个结构可以节省时间和空间。
修改show_polar函数:
- 调用函数时,传递结构的地址
&pplace
而非结构本身pplace
; - 形参声明为指向polar的指针,即
polar *
。(此处不修改结构,因此使用const,若需修改则不加const); - 形参为指针,使用间接成员运算符
->
而非成员运算符.
。
函数声明和定义:
void show_polar(const polar *pda);
void show_polar(const polar *pda)
{
cout << “distance is ” << pda->distance << endl; //指针通过“->”访问属性
cout << “angele is ” << pad->angle << endl;
}
函数调用:
polar pplace;
… //给pplace赋值,
show_polar(&pplace);
7.7 函数和string对象
c-风格字符串和string对象的用途几乎相同,较之数组,string对象与结构更为相似。可以直接赋值给另一个对象,也可以作为完整的实体传递给函数。如果需要多个字符串,可以声明一个string对象数组,而非二维char数组。
void display(const string sa[], int n);
void display(const string (*sa)[], int n);
以上两种方式等价,形参sa均为一个指向string对象的指针,因此sa[i]是一个string对象。
7.8 函数与array对象
语句:
cin >> (*pa)[i];
pa是一个指向array<double, 4>对象的指针,并非array对象,所以不可像操作数组那样操作pa。*pa
才为这种对象,*pa
才可以像数组那样操作,即(*pa)[i]
才是对象的一个元素。
7.9 递归
7.9.1 包含一个递归调用的递归
语法格式:
void recurs(argumentlist)
{
statements1
if (test)
recurs(arguments)
statements2
}
上述recurs即为一个递归函数。通常将递归调用放在if语句中,上述test最终将为false,调用将断开。只要if语句为true,每个recurs()调用都将执行statement1,然后再调用recurs(),而不会执行statement2;当if为false,将执行statement2部分,然后结束,并将控制权返回给前一个调用。以此类推。
上述recurs()若进行5此递归,则statement1将按函数调用的顺序执行5次,statement2则以与函数调用相反的顺序执行5次。
void countdown(int n)
{
cout << “counting down …” << endl; //statement1
if(n>0)
countdown(n-1);
cout << “n” << “; kadoom” << endl; //statement2
}
//打印结果:
counting down …4
counting down …3
counting down …2
counting down …1
counting down …0
0:kadoom
1:kadoom
2:kadoom
3:kadoom
4:kadoom
7.9.2 包含多个递归调用的递归
需要将一项工作不断分为两项较小的类似的工作(二叉树)时使用递归十分有用。递归又叫分治法。
递归层次较少时推荐使用递归,较多时复杂度较高,不推荐递归。
7.10 函数指针
7.10.1 函数指针的基础知识
例如,设计estimate()
函数,估算编写指定行数的代码所需时间,并且希望不同的程序员都将使用该函数。对于所有用户,estimate()中一部分代码是相同的,但该函数允许每个程序员提供自己的算法来估算时间。
为实现上述功能,将采用传递程序员要使用的算法函数的地址给estimate()
的机制。步骤如下:
获取函数地址
声明一个函数指针
使用函数指针来调用函数
- 获取函数地址
使用函数名(后边不跟参数)即可。
例如think()
是一个函数,其地址便为think
,think()
为返回值。
process(think); //传递的是think()函数的地址
thought(think()); //传递的是think()函数的返回值
process()
调用使得process()
函数可在其内部调用think()函数;thought()
调用首先调用think()
函数,然后将think()
函数的返回值传递给thought()
函数。
2) 声明函数指针
声明指向函数的指针时,必须指定指针指向的函数类型,即声明应像函数原型那样指出有关函数的信息。
例如:
//pam函数原型:
double pam(int);
//指针类型声明:
double (*pf)(int);
pam
是函数,(*pf)
也是函数。而(*pf)
是函数,pf
便是函数指针。
要声明指向特性类型的函数指针,通常可先写这种函数的原型,然后用(*pf)
替换函数名。
注意必须使用()
将*pf
括起来。*pf(int)
意味着pf()
是一个返回指针的函数,而(*pf)int
表示pf
是一个指向函数的指针。
正确声明pf
后,即可将函数的地址赋给它:
pf = pam; //pam()的形参列表和返回类型必须和pf相同。
estimate()
函数调用pam
算法,将要编写的代码行数和估算算法(如pam()
)的地址传递给estimate()
,对应的函数原型为:
//第二个参数是一个函数指针,指向一个结构int参数,并返回double值。
void estimate(int lines, double (*pf)(int));
//调用时传递pam()函数的地址pam:
estimate(20, pam)
- 使用指针来调用函数
(*pf)
其角色与函数名相同,即将其看做pam
即可:
double pam(int);
double (*pf)(int);
pf = pam;
double x = pam (4);
double y = (*pf) (5);
double z = pf(5);
7.10.2 函数指针示例
7.10.3 深入探讨函数指针
以下函数原型的形参列表和返回类型相同:
const double * f1(const double ar[], int n);
const double * f2(const double [], int);
const double * f3(const double *, int);
#include<iostream>
using namespace std;
const double *fi(const double *ar, int n);
const double *f2(const double ar[], int n);
const double *f3(const double ar[], int n);
int main(void)
{
double av[] = {1112.3, 1542.6, 2227.9}
//part1
//p1(p2):pointer to a function
const double *(*p1) (const *, int) = f1;
//c++11:auto //自动判断其类型
auto p2 = f2;
cout << “part1: …..” << endl;
cout << “address value” << endl;
cout << (*p1)(av, 3) << “; ” << *(*p1)(av, 3); //(*p1)(av, 3) == f1(av, 3)
cout << p2(av, 3) << “; ” << *p2(av, 3); // p2(av, 3) == f2(av, 3)
//part 2
//pa(pb) is an array of pointers
//每个元素都是一个函数值
const double *(*pa[3])(const double *, int) = {f1, f2, f3};
auto pb = pa;
cout << “part2: …..” << endl;
cout << “address value” << endl;
for(int i = 0; i < 3; i++)
cout << pa[i](av, 3) << “; ” << *pa[i](av, 3) << endl; //调用f1,f2,f3
for(int i = 0; i < 3; i++)
cout << pb[i](av, 3) << “; ” << *pb[i](av, 3) << endl; //调用f1,f2,f3
//part 3
//pc(pd): a pointer to an array of function pointers
auto pc = &pa; //pa是三个函数组成的数组;所以pc是一个指向三个函数作为元//素的数组的指针
//上式等价于:const double*(*(*pc)[3])(const double *, int) = &pa;
const double *(*(*pd)[3])(const double *, int ) = &pa;
cout << “part3: …..” << endl;
cout << “address value” << endl;
cout << (*pc)[0](av, 3) << “; ” << *(*pc)[0](av, 3) << endl; //(*pc) == pa -->(*pc) //[0](av, 3) == pa[0](av, 3)
const double *pdb = (*pd)[1](av, 3) //(*pd)[1](av, 3) == pa[1](av, 3) == f2(av, 3)
cout << pdb << “; ” << *pdb << endl;
//cout << (*pd)[2](av, 3) << “; ” << * (*pd)[2](av, 3) << endl;
cout << (*(*pd)[2])(av, 3) << “; ” << *(*(*pd)[2])(av, 3) << endl; //与上式等价
return 0;
}
const double *fi(const double *ar, int n)
{
return ar;
}
const double *f2(const double ar[], int n)
{
return ar+1; //第二个元素地址
}
const double *f3(const double ar[], int n)
{
return ar+2;
}
区别:
(*pa[2])(av, 3) | *pa[2](av, 3) |
---|---|
f3(av,3) | *(f3(av, 3)) |
第三个元素地址 | 第三个元素的值 |
注意:大多数情况下,pa
都是数组第一个元素的地址,即&pa[0]
,是单个指针的起始地址;但&pa
是整个数组(即三个指针块的地址)的起始地址。数值上,pa
和&pa
相同;但其类型不同,pa+1
为数组中下一个元素的地址,&pa+1
为数组pa
后面一个12字节内存块的地址(此处假定地址为4字节),其次,要得到第一个元素的值,只需对pa
解除一次引用,而&pa
需解除两次引用,即:
**pa = *pa = pa[0];
7.10.4 使用typedef进行简化
声明变量时,加上typedef
,即可将变量声明为类型别名,语法格式:
typedef double n;
n不再是变量,而是double类型的别名。
typedef const double *(*p_fun)(const double *, int);
p_fun p1 = f1;
p_fun
不再是函数指针变量,而是函数指针类型,p1
即为函数指针。以此简化代码:
p_fun pa[3] = {f1, f2, f3};
p_fun (*pd)[3] = &pa;