1.基本知识
①函数定义
C++对返回类型有限制:不能是数组,但可以是其他类型,甚至是结构体和对象(可以将数组作为结构或对象组成部分来返回)。
②函数原型
函数原型描述了函数到编译器的接口,它将函数返回类型以及参数类型和数量告诉编译器,使得编译器能够正确检查出这些错误,极大降低程序出错的几率。
函数原型中参数列表不要求提供变量名,只需要提供类型列表就好了,但是提供变量名将使得原型更容易理解。
C++自动将传递的值转换为原型中指定的类型(两者都是算术类型,并且转换是有意义的,即保证程序按照想法正确运行),在编译阶段进行的原型化被称为静态类型检查。
③函数调用
2.函数参数和按值传递
①按值传递:
在C++中,使用参数表示实参,参量表示形参。值传递就是将参数的值传递给形参,函数执行的操作将不会影响main()函数中参数的值。
②多个参数
函数可以有多个参数,用逗号将这些参数分隔开即可。
技巧:
1.cin>>tatal>>choice;连续输入
2.多个数字相乘且相除,不要先把乘的算完再算除法,中间值会很大,建议先乘一个数,再除一个数,中间值不会很大,防止越界。
3.函数和数组
①函数接口
函数是处理复杂的类型(如数组和结构)的关键,下面介绍如何将数组和函数结合在一起。
用函数来处理数组,函数的参数至少要为数组名和数组的长度:
int sum_arr(int arr[],int size)
方括号指出arr是一个数组,方括号为空则表示可以将任何长度的数组传递给该函数。但实际情况是arr不是数组,而是一个指针,但在编写函数时,依然可以将它当作数组来使用。
1.函数如何使用指针来处理数组?
我们知道,数组名实际上是数组第一个元素的地址,因此函数传递的是地址,所以正确的函数头应该是这样:
int sum_arr(int* arr,int size)
事实上,这两个声明都是正确的,当且仅当用于函数头和函数原型时 int * arr = int arr[ ].但建议采用数组的形式来声明,这提示用户,arr不进指向int,还指向int数组的第一个int。
请记住下边两个等式:
arr[i] = *(arr+i);
&arr[i] = arr+i;
2.将数组作为参数意味着什么?
传递常规变量时,函数将使用变量的拷贝,但传递数组时,函数将使用原来的数组,这并没有违反C++按值传递的方法,只是因为形参和实参因为传递的是地址,指向的是同一个东西而已。
并且,因为传递的参数只有数组首元素的地址和数组的大小,函数缺少一些关于原始数组的知识,例如,他不能使用sizeof来获悉原始数组的长度(只能得到指针的大小),而只能依赖传入的第二个参数,所以我们可以“欺骗”函数,使得它处理一整个数组的部分元素(前三个元素,或者第四个元素后的所有元素等)。
②数组函数示例
编写特定的函数来处理特定的数据操作使得函数模块化,这很重要。假设我们现在要使用一个数组来记录房地产的价值。
思路:(先考虑函数头,在考虑函数功能和其他输入的情况)
1.首先需要确定使用哪种类型。
double类型的取值范围比 int 和 long 大,并且提供足够多的有效位数保证精度。
接下来思考数组元素的数目,假设房地产数目不超过5个,则可以使用一个包含5个元素的double数组。
2.考虑对房地产数组进行的操作。
两个基本操作是填充和显示,我们再添加一个操作:重新评估每种房地产的值,简单起见,假设所有房地产以相同的比率增加或减少。
介绍每个函数的步骤,最后进行整合:
1.填充数组:
改变数组的值,所以不能用const;
通常要管理多个人的投资,因此需要多个数组,数组不能设置固定长度,由第二个参数决定;
用户希望数组被填满时停止读取数据,具备这种特性;
由于用户输入的数量小于数组长度,因此返回实际输入元素的数目;
数组如何提前停止输入? 1)特殊值来结束循环——这里因为正常输入均为正值,我们采用输入负值结束循环。
此外,程序需要对错误输入作出反应——如停止输入等。
#include<iostream>
int fill_array(double arr[], int n);
int main()
{
return 0;
}
int fill_array(double arr[], int n)
{
using namespace std;
int i = 0;
double temp;
//填充数组
for (i = 0; i < n; i++)
{
cout << "Enter value:\n" << "# " << i + 1 << ":"; //提示语要有
cin >> temp;
//处理错误输入
if (!(cin))
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Wrong input!input process terminated.\n";
break;
}
//处理提前结束情况
else if (temp < 0)
{
break; //结束填充
}
//均不是上边两种情况
arr[i] = temp;
}
//填充结束,返回真正填充的数量
return i; //循环最后一步是 i+1,所以直接返回i;
}
2.显示数组及应用const保护
显示函数很简单,只需要将数组名和数组大小传递给函数即可,但是有另一个要考虑的问题,我们仅仅是显示数组,不希望函数修改数组值,所以应用指向const的指针来对数据进行保护。
//显示函数
void show_array(const double arr[], int size)
{
for (int i = 0; i < size; i++)
{
std::cout << "Property: #" <<i+1<<"$:"<< arr[i] << std::endl; //提示语很重要
}
}
3.修改数组:
函数参数应该包含所乘的比例,所以有三个参数;
函数修改数组的值,不需要返回值;
要改变数组的值,所以不能使用const保护;
void revalue(double r, double arr[], int size)
{
for (int i = 0; i < size; i++)
arr[i] *= r;
}
4.将上述代码组合起来
已经完成了所有处理数组的函数,因此main()函数的工作很简单,只需要判断在更新房地产价值时用户输入是否为数字,如果不是,则要求用户重新输入。
#include<iostream>
int fill_array(double arr[], int n);
void show_array(const double arr[], int size);
void revalue(double r, double arr[], int size);
int main()
{
using namespace std;
const int Max = 5;
double properties[Max];
double factor = 0;
int size = fill_array(properties, Max);
show_array(properties, size);
//改变值
if (size > 0) ////改变值的前提是数组存在
{
cout << "Enter revaluation factor:";
cin >> factor;
if (!cin) //判断输入是否为正确
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Bad input,enter revaluation factor:";
cin >> factor;
}
revalue(factor, properties, size);
show_array(properties, size);
}
cout << "done!" << endl;
return 0;
}
//填充数组
int fill_array(double arr[], int n)
{
using namespace std;
int i = 0;
double temp;
//填充数组
for (i = 0; i < n; i++)
{
cout << "Enter value:" << "# " << i + 1 << ":"; //提示语要有
cin >> temp;
//处理错误输入
if (!(cin))
{
cin.clear();
while (cin.get() != '\n')
continue;
cout << "Wrong input!input process terminated.\n";
break;
}
//处理提前结束情况
else if (temp < 0)
{
break; //结束填充
}
//均不是上边两种情况
arr[i] = temp;
}
//填充结束,返回真正填充的数量
return i; //循环最后一步是 i+1,所以直接返回i;
}
//显示函数
void show_array(const double arr[], int size)
{
for (int i = 0; i < size; i++)
{
std::cout << "Property: #" <<i+1<<"$:"<< arr[i] << std::endl; //提示语很重要
}
}
//修改数组的值
void revalue(double r, double arr[], int size)
{
for (int i = 0; i < size; i++)
arr[i] *= r;
}
③指针和const
1.指针指向一个常量对象
声明方式: const typename * 指针name = 变量地址;
举例说明:
int age = 39;
const int *pt = &age;
pt指向一个const int (39),因此不能用*pt来修改age的值,但是可以通过age直接修改自己的值。
pt的声明并不意味着它指向的值是一个常量,只是意味着对pt而言,这个值是常量。
*pt的值不能被修改,但是pt的值可以被修改,可以改变pt的值,使pt指向不同的值。
注:
1.如果数据类型本身并不是指针,则可以将const数据或非const数据的地址赋给指向const的指针,只能将非const数据的值赋给非const指针。当数据是指针类型时,将const和非const指针混合赋值方式将不在安全,不推荐这样。
2.尽可能使用const:1)避免由于无意间修改数据而导致的编程错误,同时也使得函数可以处理const和非const实参,否则只能接受非const数据,所以如果条件允许,则应该将指针形参声明为指向const的指针。
2.常量指针
声明方式: typename * const 指针name = 变量地址;
举例说明:
int sloth = 3;
const int *ps = &sloth;
int * const finger = &sloth;
该声明使得finger只能指向sloth,但允许使用finger来修改sloth的值。
还可以声明指向const对象的const指针。通常,将指针作为函数参数来传递,可以使用指向const的指针来保护数据,如果数据基本元素是指针或者指向指针的指针,则不能使用const来保护数据。
4.函数和二维数组
将二维数组作为形参,应该如何声明函数原型呢?
int data[3][4] = { {1,2,3,4},{9,8,7,6},{2,4,6,8} };
int total = sum(data,3);
sum()函数的原型怎么声明呢?
1.指针方法:
int sum( int (*ar2)[4],int size )
第一个参数是一个指向有着4个int数组的指针,是一个指针,所以要给*ar2带上括号,不然就是个指针数组(函数参数不能是数组,一维数组的参数本质也是个指针)
2.数组方法——可读性更高
int sum(int ar2[][4],int size)
注:以上两种方法列数都是固定的,并且不能使用const,const只能用于指向基本类型的指针,这里是指向指针的指针,所以不能用const限定符。
数组方法如何理解呢?
ar2+r ——> 指向第r行数组
*(ar2+r) = ar2[r] ——>第r行数组——>指向 第r行数组 第一个元素
*(ar2+r)+n ——>指向 第r行数组 第n个元素
*( *(ar2+r)+n ) = ar2[r][n] 第r行数组 第n个元素
5.函数和C-风格字符串
①将C-风格字符串作为参数的函数
将字符串作为参数传递给函数,表示字符串的方法有三种:
1.char数组
2.用双引号括起来的字符串字面量
3.被设置为字符串的地址的char指针
本质上都是在传递字符串的第一个字符的地址。
C风格字符串与常规char数组一个重要区别是:有一个空字符作为结束字符。因此在参数传递时,不需要传递字符串的长度,函数可以使用循环一次检查字符串是否结束。
#include<iostream>
int c_in_str(const char* str, char ch);
int main()
{
using namespace std;
char mmm[15] = "miniunm";
const char *wail = "ululate"; //不加const 无法实现指向字符串常量的指针
int ms = c_in_str(mmm, 'm');
int us = c_in_str(wail, 'u');
cout << ms << " m character in " << mmm << endl;
cout << us << " u characters in" << wail << endl;
return 0;
}
int c_in_str(const char* str, char ch)
{
int count = 0;
while (*str) //直接进行判断
{
if (*str == ch)
count++;
str++;
}
return count;
}
②返回C-风格字符串的函数
函数无法返回一个字符串,但可以返回字符串的地址。将函数返回类型定义为一个指向字符的指针,便可以返回一个字符串。
#include<iostream>
char* bulider(char c, int n);
int main()
{
using namespace std;
char ch;
int times;
cout << "Enter a character:";
cin >> ch;
cout << "Enter a number:";
cin >> times;
char* p = bulider(ch, times);
cout << p;
delete[] p; //new创建的数组空间还没有被释放
return 0;
}
char* bulider(char c, int n)
{
char* pt = new char[n + 1];
pt[n] = '\0';
while (n-- > 0)
pt[n] = c;
return pt; //pt这个指针空间被释放了,new所使用的空间还没被释放
}
6.函数和结构
为结构体编写函数比为数组编写函数要简单得多。结构变量的行为更接近于基本的单值变量,也就是说结构体被视作一个整体。前边讲过,可以将结构赋给另一个结构,同样的,也可以按值传递的结构,就像普通变量那样。另外,函数也可以返回结构,与数组名不同的是,结构体的名称就是名称,要想获得结构体的地址,需要用到 取地址符号(&)。
使用结构体编程的时候,值传递有时会使用大量内存(值传递需要有副本),出于这些原因,C++提供第三种选择——按引用传递,第8章介绍。本章先介绍值传递和地址传递。
①值传递
当结构比较小的时候,按值传递结构最为合理。
方法:将结构体就当成一个标准的类型名,去声明变量,函数返回类型和函数的参数类型。
1.返回相同的结构
#include<iostream>
//结构体声明必须在结构体函数之前
struct travel_time
{
int hours;
int mins;
};
travel_time sum(travel_time t1, travel_time t2);
void show(travel_time t1);
//定义结构体
int main()
{
travel_time t1 = { 3,21 };
travel_time t2 = { 4,52 };
travel_time total = sum(t1, t2);
show(total);
return 0;
}
//结构体函数
travel_time sum(travel_time t1, travel_time t2)
{
travel_time total;
total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / 60;
total.mins = (t1.mins + t2.mins) % 60;
return total;
}
void show(travel_time t1)
{
std::cout << "hours:" << t1.hours << std::endl;
std::cout << "minutes:" << t1.mins << std::endl;
}
2.返回不同的结构
#include<iostream>
struct rect
{
double x;
double y;
};
struct polar
{
double dist;
double angle;
};
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);
int main()
{
rect rplace;
polar pplace;
using namespace std;
cout << "Enter the x and y values:";
while (cin >> rplace.x >> rplace.y)
{
pplace = rect_to_polar(rplace);
show_polar(pplace);
cout << "Enter the x and y values:";
}
cout << "done";
return 0;
}
polar rect_to_polar(rect xypos) //一个结构体参数,函数返回值为另一个结构体
{
polar dapos;
dapos.dist = sqrt(xypos.x * xypos.x + xypos.y * xypos.y);
dapos.angle = atan2(xypos.y, xypos.x)*57.29577951; //计算xy角度并将弧度转换为度
return dapos;
}
void show_polar(polar dapos)
{
std::cout << " distance: " << dapos.dist << std::endl;
std::cout << " angel: " << dapos.angle << std::endl;
}
②地址传递
使用传递结构的地址而不是整个结构以节省空间和时间,函数参数为指向结构体的指针。
由于形参是指针而不是结构,因此应该 使用间接成员运算符(->),而不是成员运算符(.)。
如果不需要结构体内容,记得加上const限定。
如果是两个不同类型的结构,则参数列表变为2个,一个提供原值,一个用于存储,函数返回值为空。
#include<iostream>
struct rect
{
double x;
double y;
};
struct polar
{
double dist;
double angle;
};
void rect_to_polar(const rect* pxy, polar* pda);
void show_polar(const polar* pda);
int main()
{
rect rplace;
polar pplace;
using namespace std;
cout << "Enter the x and y values:";
while (cin >> rplace.x >> rplace.y)
{
rect_to_polar(&rplace,&pplace); //直接取地址,函数参数本质就是地址,而不是指针,请把结构体当作int一样的东西
show_polar(&pplace);
cout << "Enter the x and y values:";
}
cout << "done";
return 0;
}
void rect_to_polar(const rect* pxy, polar* pda) //一个结构体参数,函数返回值为另一个结构体
{
pda->dist = sqrt(pxy->x * pxy->x + pxy->y * pxy->y);
pda->angle = atan2(pxy->y, pxy->x)*57.29577951; //弧度转换为度
}
void show_polar(const polar* dapos)
{
std::cout << " distance: " << dapos->dist << std::endl;
std::cout << " angel: " << dapos->angle << std::endl;
}
7.函数和string对象
虽然C-风格字符串和string对象的用途几乎相同,但与数组相比,string对象与结构更类似。例如,string对象和结构体都能直接赋值给另一个string对象和结构体,可以将结构体 string对象作为完整实体传递给函数。
如果需要多个字符串,可以声明string 对象数组,而不是二维char数组。
#include<iostream>
#include<string>
void display(const std::string str[], int n);
int main()
{
using namespace std;
string list[5];
cout << "Enter your string:\n";
for (int i = 0; i < 5; i++)
{
cout << "#" << i + 1 << ":";
getline(cin, list[i]);
}
cout << "Your list:\n";
display(list,5);
return 0;
}
void display(const std::string str[],int n)
{
for (int i = 0; i < n; i++)
std::cout << str[i] << std::endl;
}
重点就是把string 当成int一样的类型去使用
8.函数和array对象
将array作为函数参数进行传递多用第8章引用传递的方法,这里只是简要介绍当array对象作为函数参数进行值传递和地址传递。
#include<iostream>
#include<string>
#include<array>
using namespace std;
const int SIZE = 4;
void fill(array<string, SIZE>* pa);
void show(array<string, SIZE> pa);
//结构体声明。让后边函数可见
array<string, SIZE>sname =
{
"Spring","Summer","Fall","Winter"
};
int main()
{
array<string, SIZE>sexpense;
cout << "Enter your expense:\n";
fill(&sexpense);
show(sexpense);
return 0;
}
void fill(array<string, SIZE> *pa) //数组大小是固定的4个,是否可以改变还不清楚
{
for (int i = 0; i < SIZE; i++)
{
cout << "Enter " << sname[i] << " expense :";
getline(cin,(*pa)[i]); //pa[i]是string地址,所以要先对pa进行解引用,才能得到string,而不是它的地址
}
}
void show(array<string, SIZE> pa)
{
for (int i = 0; i < SIZE; i++)
{
cout <<sname[i]<<": $"<< pa[i] << endl;
}
}
9.递归
递归函数调用自己,则被调函数也将调用自己,这将无限循环下去,除非代码有包含终止调用链的内容。通常的方法将递归调用放在if语句中。
void recurs(arguments)
{
statements1
if(test)
recurs(arguments)
statements2
}
只要if语句为true,每个recurs()调用都将执行语句1,然后继续调用recurs(),当if语句为false时,当前调用执行语句2.当前调用结束后,程序控制权返回给调用它的recurs(),而该recurs()将执行其语句2的部分。因此,如果一个recurs()进行5次调用,语句1按函数调用顺序执行5次,语句2按照与函数调用相反的顺序执行5次。并且,每个递归都会创建一套属于自己的变量,多次调用时会很占用内存,所以在递归层次较少的时候,这是个好方法,如果递归层次过多,这是一个糟糕的选择。
示例1——包含一个递归调用的递归
#include<iostream>
void countingdown(int n);
int main()
{
int n = 4;
countingdown(4);
return 0;
}
void countingdown(int n)
{
std::cout << "Counting down ..." << n<<std::endl;
if (n > 0)
countingdown(n - 1);
std::cout << n << ": Kaboom!\n";
}
示例2——包含多个递归调用的递归
#include<iostream>
void subdivide(char ch[], int low, int high, int level);
int main()
{
const int SIZE = 66;
const int level = 6;
char ch[SIZE];
//填充第一层数组,原始数组
for (int i = 1; i < SIZE-2; i++)
ch[i] = ' ';
ch[0] = ch[SIZE - 2] = '|';
ch[SIZE - 1] = '\0';
//分层准备
int low = 0;
int high = SIZE - 2;
//开始分层
for (int j = 1; j <= level; j++)
{
subdivide( ch, low, high,j);
std::cout << ch << std::endl;
for (int i = 1; i < SIZE - 2; i++)
ch[i] = ' '; //重新回到第一层数组状态,因为最初|没有变,只需填充空格就好
}
return 0;
}
void subdivide(char ch[], int low, int high, int level)
{
if (level == 0)
return;
int mid = (low + high) / 2;
ch[mid] = '|';
subdivide(ch, low, mid, level - 1);
subdivide(ch, mid, high, level - 1);
}
10.函数指针
仅简单介绍函数指针,完整的介绍留给更高级的C++书。
函数也有地址,函数的地址是存储其机器语言代码的内存的开始的地址,可以编写将另一个函数的地址作为参数的函数,这样的方法与直接调用比起来显得很笨拙,但它允许在不同的时间内传递不同的函数地址,这意味着在不同的时间使用不同的函数。
①函数指针基础知识
1.获取函数的地址
函数的地址就是函数名(后边不跟参数)。要将函数作为参数进行传递,必须传递函数名。
2.声明函数指针
声明应该指定函数的返回类型以及函数的特征标(参数列表),也就是说,声明应该像函数原型那样指出有关函数的信息。通常,要声明指向特定类型的函数指针,可以首先编写这种函数的原型,然后用(* pf)替代函数名,这样pf就是函数的指针。
double pam(int);
double (*pf) (int);
pf = pam; //赋值
为了提供正确的运算优先级,必须在声明中用括号将*pf括起来。括号的运算优先级比*运算符高,因此*pf(int)意味着pf()是一个返回指针的函数,而(*pf)(int)意味着pf是一个指向函数的指针。
注意,pam()的特征标和返回类型必须与pf相同,如果不相同,编译器将拒绝这种赋值。
3.使用指针来调用函数
(*pf)扮演的角色与函数名相同,因此使用(*pf)时,只需要将他看作函数名即可;实际上,C++也允许像函数名那样使用pf(由于函数名是指向该函数的指针,指向函数的指针的行为应该与函数名相似)。
double x = pan(4);
double y = (*pf)(5);
double z = pf(6);
示例:
#include<iostream>
double betsy(int n);
double pam(int n);
void estimate(double (*pf)(int), int lines);
int main()
{
using namespace std;
cout << "How many lines do your programm has?\n";
int lines;
cin >> lines;
cout << "Here's betsy's estimate:\n";
estimate(betsy, lines);
cout << "Here's pam's estimate:\n";
estimate(pam, lines);
return 0;
}
double betsy(int n)
{
return 0.05 * n;
}
double pam(int n)
{
return 0.03 * n + 0.0004 * n * n;
}
void estimate(double (*pf)(int), int lines)
{
std::cout << lines << " lines needs " << pf(lines) << " hours." << std::endl;
}
②深入探讨函数指针
函数指针的表示非常的恐怖,非常多层。
const double * f1(const double ar[],int n);
const double * f2(const double[],int);
const double * f3(const double*,int);
三个函数的函数特征标和返回类型均相同,假设要声明一个指针,指向这三个函数其中之一,假设指针名为p1.
const double * (*p1)(const double ar[],int n);
p1 = f1;
auto p2 = f2; //C++11的自动推断很方便
cout<<(*p1)(av,3) //指向调用函数,显示函数的返回值(double *)也就是double值的地址
<<*(*p1)(av,3) //查看double的值
鉴于我们要指向三个函数,如果有一个函数指针数组将很方便,落脚点在数组,所以数组的声明为:(pa首先是个数组,[]运算优先级高于*)
const double * (*pa[3])(const double ar[],int n)={f1,f2,f3};
auto pb = pa;
const double * px = (*pa[0])(av,3); //地址解引用
const double * py = pa[0](av,3); //函数名是地址
//获得 px py指向的值的大小
double x = *(*pa[0])(av,3);
double y =*pa[0](av,3); //不需要加括号,因为按照优先级,本来就是后边算完,最后解引用
注:这里不能使用auto,自动类型推断只能用于单值初始化,而不能用于初始化列表,但在声明数组pa后,就可以用auto来声明pb;
前边说过,数组名是数组第一个元素的指针,因此pa pb均为指向函数指针的指针。如何用他们来调用函数呢?上边代码写过了。
可做的另一件事就是创建指向这个数组的指针,新建立的指针是指向数组指针的指针,落脚点在于指针,因为不是数组,这意味着他是个单值,所以我们可以采用auto;如果自己书写的话,它本质是个指针,所以应该先和*结合,也就是要加括号。
auto pc = &pa;
const double *(* (*pd)[3]))(const double *,int) = &pa;
const double * p = (*pd)[i](av,3); //函数调用
const double * p = ((*pd)[i])(av,3); //函数调用
pd指向数组,*pd是数组,*pd[i]是数组中的元素,即函数指针。函数调用在上边代码里。
pa和&pa的差异:
1.pa是数组第一个元素的地址,&pa是整个数组地址,二者在值上一样,但是类型不一样(单个指针地址 和指针数组的地址)
2. pa+1是下一个值;&pa+1是后面12个字节内存块地址
3,得到第一个元素的值 *pa =**&pa=pa[0].
#include<iostream>
const double* f1(const double ar[], int n);
const double* f2(const double ar[], int);
const double* f3(const double* ar, int);
int main()
{
using namespace std;
double av[3] = { 111.2,133.6,654.7 };
//指针来输出
cout << "Using pointer to functions:\n";
const double* (*pd1)(const double ar[], int n);
pd1 = f1;
auto pd2 = f2;
auto pd3 = f3;
cout << "Address value:\n"
<< (*f1)(av, 3) << " " << *(*f1)(av, 3) << endl;
cout << (*f2)(av, 3) << " " << *(*f2)(av, 3) << endl;
cout << (*f3)(av, 3) << " " << *(*f3)(av, 3) << endl;
cout << "Using an array of pointers to functions:\n";
const double* (*pa[3])(const double ar[], int n) = { f1,f2,f3 };
cout << "Address value:\n"
<< pa[0](av, 3) << " " << *pa[0](av, 3) << endl;
cout << (*pa[1])(av, 3) << " " << *(*pa[1])(av, 3) << endl;
cout << pa[2](av, 3) << " " << *pa[2](av, 3) << endl;
cout << "Using an pointer of pointers to functions:\n";
auto pb = &pa;
cout << "Address value:\n"
<< (*pb)[0](av, 3) << " " << *(*pb)[0](av, 3) << endl;
cout << ( * (*pb)[1])(av, 3) << " " << *(*(*pb)[1])(av, 3) << endl;
cout << (*pb)[2](av, 3) << " " << *(*pb)[2](av, 3) << endl;
cout << "Using pointers to an array of pointers:\n";
const double* (* ((*pc)[3]))(const double ar[], int n) = &pa;
cout << "Address value:\n"
<< (*pc)[0](av, 3) << " " << *(*pc)[0](av, 3) << endl;
cout << (*(*pc)[1])(av, 3) << " " << *(*(*pc)[1])(av, 3) << endl;
cout << (*pc)[2](av, 3) << " " << *(*pc)[2](av, 3) << endl;
return 0;
}
const double* f1(const double ar[], int n)
{
return ar;
}
const double* f2(const double ar[], int)
{
return ar + 1;
}
const double* f3(const double * ar, int )
{
return ar+2;
}
主要就是搞清 函数指针引用两种格式,解引用优先级低于数组(如果是指针,记得加括号)这两点即可。
③使用typedef进行简化
感谢auto,使得我们的声明变得简单,同时,我们还可以使用typedef来减少输入量。
typedef double real; //real 是 double 另外一个名字
typedef const double * (*p_fun)(const double*,int) //p_fun是该类函数指针别名
p_fun p1 = f1; //定义函数指针;
p_fun pa[3] ={f1,f2,f3}; //定义函数指针数组
p_fun* (*pb)[3] = &pa;