[函数]——C++ Prime Plus ch7

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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值