(九十九)函数指针

与数据项(比如int、string、char等)相似,函数也有地址。

 

函数的地址是存储其机器语言代码的内存的开始地址(不懂)。

 

这些地址对用户而言,没什么用,也不重要,但对程序而言,却很有用。

 

例如,可以编写将另外一个函数的地址作为参数的函数。这样,第一个函数将能够找到第二个函数,并运行它。

与直接调用第二个函数相比,这种方法很笨拙,但他允许在不同的时间传递不同函数的地址,这意味着可以在不同的时间使用不同的函数。

 

函数指针和指针函数的区别:

函数指针:指向某个函数的指针(即这个指针指向函数的地址);

指针函数:返回值是指针的函数

 

函数指针的基础知识:

函数的地址:

函数名 就是函数的地址(像字符串一样);

 

声明函数指针:

有些像函数返回指针,但是要把*和函数名用括号括起来。例如:函数类型 (*函数名) (参数); 

注意:①声明的函数指针,要和函数的特征标(例如类型、函数的参数类型)要相符;

②*函数名 此时是函数,而 函数名 此时是指针。

 

函数指针作为参数时:

函数类型 函数名(函数类型 (*函数名)(函数指针的参数类型) );

注:蓝色部分为函数指针作为参数时的写法。

 

调用函数指针时:

和声明函数指针类似,用函数指针的 (*函数名) 取代普通函数 函数名 即可。

如:(*函数名)(函数指针的参数)  这样。

也可以像使用普通函数那样,通过  函数名(参数)  这样调用函数,C++是允许这样做的。

 

给函数指针赋值时:

首先,函数指针和函数的特征标要相符。

然后例如代码节选:

void ab(int);//函数原型

void(*c)(int);
//函数指针原型

c = ab; //让函数指针c指向函数ab。这行代码一般在函数里

这样。

 

 

无返回值、将函数指针赋值、声明函数指针、调用函数指针,如代码:

#include<iostream>
using namespace std;
void ab(int);	//函数1
void ef(int);	//函数2
void(*c)(int);	//声明函数指针,注意,这行代码放在main()函数内也可以

int main()
{
	int a = 5;
	c = ab; //函数指针指向函数1
	c(a);	//调用函数指针(即执行函数1)
	c = ef;	//函数指针指向函数2
	(*c)(a);	//调用函数指针(此时执行函数2),注意c和(*c)是等价的,不能省略括号
	system("pause");
	return 0;
}

void ab(int m)
{
	cout << "m = " << m << endl;
}

void ef(int m)
{
	cout << "m = " << m * 2 << endl;
}

输出:

m = 5
m = 10
请按任意键继续. . .

解释:

①这行代码就是函数指针指向不同函数时,同样的变量作为参数,却执行不同的函数。

 

 

有返回值、将函数指针作为函数的参数、不同情况下指向不同的函数、使用函数的地址,如代码:

#include<iostream>
using namespace std;
int ab(int);	//函数1
int ef(int);	//函数2
int c(int(*x)(int),int y);	//引入函数指针作为函数c的参数

int main()
{
	int a = 5;
	int m, n;
	m = c(ab, a);	//调用的时候,指定ab函数作为参数
	n = c(ef, a);	//调用的时候,制定ef为指定的函数
	cout << "m = " << m << endl;
	cout << "n = " << n << endl;
	system("pause");
	return 0;
}

int ab(int m)
{
	return m + 3;
}

int ef(int m)
{
	return m * 3;
}

int c(int(*x)(int),int y)	//x指向某个函数,y为函数将使用的参数。因此,在调用的时候,需要指定使用哪个函数。
{
	return x(y);	//返回 调用指针指向的函数(参数)的返回值
}

输出:

m = 8
n = 15
请按任意键继续. . .

总结:

①以上两部分代码,涉及到了函数的地址(即函数名)、声明函数指针、函数指针作为参数、调用函数指针、给函数指针赋值 这五个概念;

 

②可以在函数内部声明函数指针,然后根据变量的不同,让指针指向其他不同的函数,再执行对应的函数,并将对应函数的返回值作为函数的返回值。

如代码:

int c(int y)
//x指向某个函数,y为函数将使用的参数。因此,在调用的时候,需要指定使用哪个函数。

{

int(*x)(int);
//声明函数指针x

if(y<10)x=ab; else x=ef;
//不同参数y,让x指向不同的指针

return x(y);
//调用函数指针,y作为参数,返回值作为函数c的返回值

}

 

③A函数指针作为参数时,可以传递不同的函数名(例如B或者C)作为参数,使得在A函数中根据传递的函数名的不同,执行不同的函数(B或者C)。

 

 

 

 

当函数返回值为指针的函数指针:


函数


示例函数原型


普通函数


int  abc(int);


函数指针


int (*m) (int);


返回值为指针的函数


int* abc (int);


返回值为指针的函数指针


int* (*m) (int);

 

可以发现,无论是指向普通函数,还是指向返回值为指针的函数,函数指针都是用(*指针名)替代函数名。就像(*m)替代abc 一样。

 

不同参数让指针指向不同的函数,代码:

#include<iostream>
using namespace std;
int*m(int);
int*n(int);

int main()
{
	int*(*abc)(int);	//创建一个函数指针,面对对象是返回值是指针的函数
	int *l;	//创建一个指针,将函数的返回值(指针)赋值给他。
	for (int i = 0;i < 5;i++)
	{
		if (i % 2 == 0)	//函数指针能被2整除时,指向m,不能被2整除时,指向n
		{
			abc = m;
		}
		else
		{
			abc = n;
		}
		l = abc(i+1);	//执行函数指针,将返回值赋值给指针l。参数是i+1
		cout << i+1 << "#:" << endl;	//第(i+1)#
		cout << "l = " << l << endl << "  *l = " << *l << endl << endl;	//打印l(指针)和l指向的地址的值
	}
	system("pause");
	return 0;
}

int *m(int a)
{
	int *x = new int;	//new一个int地址出来
	*x = a;		//新地址的值是参数a
	return x;	//返回地址
}

int *n(int a)
{
	int *x = new int;	//new一个int地址出来
	*x = a*2;	//新地址的值是参数a*2
	return x;	//返回地址
}

输出:

1#:
l = 00740A10
  *l = 1

2#:
l = 00740A40
  *l = 4

3#:
l = 00740670
  *l = 3

4#:
l = 00740CA0
  *l = 8

5#:
l = 00740CD0
  *l = 5

请按任意键继续. . .

解释:

①当参数i(根据for循环而变)能否被2整除,让函数指针指向不同的函数;

 

 

 

声明一个函数指针数组:

除了上面声明一个函数指针,利用if语句使得指针指向不同函数,也可以声明一个函数指针数组,然后修改数组的成员标号,来让其指向不同的函数。

例如:

函数类型* (*函数名[函数指针成员个数]) (参数类型) ={ 函数1, 函数2};

 

可以将上面的代码

 

int*(*abc)(int);	//创建一个函数指针,面对对象是返回值是指针的函数
int *l;	//创建一个指针,将函数的返回值(指针)赋值给他。
for (int i = 0;i < 5;i++)
{
	if (i % 2 == 0)	//函数指针能被2整除时,指向m,不能被2整除时,指向n
	{
		abc = m;
	}
	else
	{
		abc = n;
	}
	l = abc(i+1);	//执行函数指针,将返回值赋值给指针l。参数是i+1
……以下略……

改为:

int*(*abc[2])(int) = { m,n };	//创建一个 函数指针 数组,成员分别指向不同的函数
int *l;	//创建一个指针,将函数的返回值(指针)赋值给他。
for (int i = 0;i < 5;i++)
{
	l = abc[i%2](i+1);	//利用i/2求余,调用不同的函数指针数组成员,并将返回值赋给指针l
……以下略……

输出结果是相同的,但减少了代码量。

原理:

①不同函数指针数组成员指向不同函数;

②然后利用成员编号(abc[i])的不同,通过i%2的结果,得到不同的函数指针成员,再指向不同的函数(m或者n)。

 

③使用函数指针数组,就像使用普通的指针数组那样,把“[ ]”加在函数名后即可。

 

④关于auto:auto的效果是根据赋值来源的类型,自动为变量提供类型推断。

例如int a=4; auto b=a; 则b也是int类型。

auto只能用于单值类型初始化,比如在上面那个代码中,加入:auto b=m;

则相当于 int*(*b)(int)=m; 这样

又因为auto只能单值,因此不能auto b={m,n}; 这样,在后面的代码是不能b[0]调用m,b[1]调用n的。

但是,可以:

int*(*abc[2])(int) = { m,n };
//创建一个 函数指针 数组,成员分别指向不同的函数

auto b = abc;

这样,在后面的代码,可以用b[i]代替abc[i]了;

 

 

函数指针的地址:

指针赋值:int a; int *b=&a; 这样的格式

 

重要:函数的名字是函数,字符串、数组的名字也只是字符串和数组;

 

只不过,在大多数情况下(例如不加地址运算符&),他被转化为指向他本身的指针。

 

 

如代码:

#include<iostream>
using namespace std;

int* a1(void);
int* a2(void);
int* a3(void);
int main()
{
	int*(*b1)(void);	//b1是一个函数指针,他可以指向a1~a3中的某一个函数
	b1 = a1;	//指针b1指向函数a1,则b1输出的是函数a1的地址,
	//b1()是调用函数b1,然后得到其返回值(int指针)。
	//*b1是b1指针指向的值(依然是函数a1,和b1等价)
	//(*b1)()和b1()是等价的。都是函数a1的返回值(这里是一个指针)
	//*b1()和*(*b1)()是等价的,都是返回值(是个指针)指向地址的值

	auto b2 = a2;
	cout << b1() << ": " << *b1() << endl;
	cout << (*b2)() << ": " << *(*b2)() << endl;

	int*(*c[3])(void) = { a1,a2,a3 };	//创建一个函数指针数组,分别指向a1,a2,a3
	//运算符[]的优先级高于*,因此先是一个数组,然后才是指针,所以是一个指针数组。而这个指针数组又是指向函数的,所以是函数指针数组

	auto d = c;	//c是一个函数指针数组,auto d=c,于是d也是一个同样类型的数组(函数指针数组,且包含3个成员)

	int *x_0 = c[0]();	//c[0]是函数指针数组的第一个成员,他指向函数a1,因此c[0]后面加上()实际上就是调用a1。函数a1的返回值是一个int指针,所以被赋值给x_0这个指针
	int *x_1 = (*d[1])();	//d[1]指向a2,加上*,函数a2被转化为一个指针,*d[1]就是*(a2),a2被转化为一个指向自己的函数,于是值还是自己。于是(*d[1])相当于a2,然后加上()就相当于调用函数a2
	//然后a2函数的返回值(int类型指针)被赋值给x_1

	int y_0 = *c[0]();	//由于()优先级比*高,因此是先调用函数a1,得到返回值(指针),然后解除指针运算,得到的是返回值指向的地址
	int y_1 = *(*d[1])();	//同上

	auto m = &c;	//m是一个指向(函数指针数组)的指针。
	//与上面的d不同,上面的d指针数组被赋值给d,因此d也是一个指针数组。
	//这里的m是指针数组的地址被赋值给m,因此m是一个指针(因为地址),他指向的是c这个指针数组。
	//因此m(一个指针,指向指针数组的地址)和*m(指针的值——指针数组的地址——指针数组第一个成员的地址),c指针数组(通常被转化为指向这个指针数组的指针)
	//&m表示为m指针的地址,&(*m)表示指针数组的地址,&c表示指针数组的地址(同第一个成员的地址),&c[0]指针数组第一个成员的地址
	cout << &m << endl;	//m指针的地址
	cout << &(*m) << endl;	//指针数组的地址
	cout << &c << endl;	//指针数组的地址
	cout << &c[0] << endl;	//指针数组第一个成员的地址,同上面
	cout << endl;
	cout << m << endl;	//指针(表示为指针指向的地址——指针数组的地址)
	cout << *m << endl;	//指针的值(指向地址的值——是函数指针数组,又通常被转化为指向他本身的指针,因此同m)
	cout << c << endl;	//指针数组,通常被转化为指向他的指针。c[0]同c
	cout << endl;

	int*(*(*n)[3])(void) = &c;	//n的类型同m
	//首先把(*n)看做一个整体,*(*n)[3]中,(*n)这个整体是一个指针数组(包含3个元素的),而n是一个指针,这个指针指向这个整体的指针数组。
	//注意对比:int*(*c[3])(void) = { a1,a2,a3 };
	//因为n这个指针指向整体的这个指针数组,所以他的值是&c,c是这个指针数组整体的地址
	//因为n指向c这个指针数组,所以*n就是指针数组(因为n指向它),因此(*n)[1]就是这个指针数组的第二个成员c[1](指向函数a2)
	cout << (*n)[1] << "和" << a2 << endl;

	system("pause");
	return 0;
}

int*  a1(void)
{
	static int a = 1;
	int*m = &a;
	return m;
}
int* a2(void)
{
	static int a = 2;
	int*m = &a;
	return m;
}
int*  a3(void)
{
	static int a = 3;
	int*m = &a;
	return m;
}

输出:

0087B034: 1
0087B038: 2
0039FE38
0039FE80
0039FE80
0039FE80

0039FE80
0039FE80
0039FE80

008713F2和008713F2
请按任意键继续. . .

解释:

①正如代码前面红字绿字所说,无论是函数、字符串、数组或者其他什么的,他们的名字,并不是地址,只是大多数情况下,被转化为指向他自己的指针。因此,假如函数名字是abc,那么abc、*abc(指向自己的指针的值,还是自己),**abc等,都是函数的地址(因为被转化为指向他自己的指针了)。

 

②&是显示他的地址。例如&函数名,显示的是函数名的地址,因此,函数a1和函数&a1(代码中没有展现),他们的值是相同的,只不过a1是通过指向他的指针(a1的地址)所展现,而&a1就是输出函数a1的地址。

 

③指针数组的声明方式是 

int*指针数组名[成员个数] = {第一个成员指向的地址, 第二个成员指向的地址, ……}

int *指针名=数组名;

是一个指向数组的指针——他解除运算后,就不是指针了。

举个简单例子,如代码:

int a[3] = { 1,2,3 };

int*b[3] = { &a[0],&a[1],&a[2] };

int*c = a;

cout << *c[0] << endl; //编译器会提示c[0]不是指针

cout << *b[0] << endl;
//但这个b[0]就没问题

c[0]能显示a[0]值的原因在于,c是一个int类型的指针,他指向a[3]这个数组,相当于指向a[3]这个数组的第一个成员,因此c输出的是数组第一个成员的地址。

 

而c[0]则输出的是这个地址的值([0]有点类似*c),c[1]则是输出这个地址往右偏移一个int距离的地址的值。同理,b[0][0]和*b[0]展现出来的值是一样的。

 

是不是这个原因不确定,但是结果根据观测,是确定的。

 

这个代码,其中指针数组b[0],b[1],b[2]分别指向不同的数组a的成员的地址,但不代表其是连续的,即b[2]-b[1]不一定等于b[1]-b[0],因为是三个指针分别赋值(指向不同的地址)。

而c[0],c[1]是连续的,因为相当于偏移了一个int的内存地址距离。因此c[3]指向的是a[2]后面一个int距离的内存地址(输出的是这个地址的值),而b[3]由于没有声明和初始化,所以b[3]是不确定的(并不一定在b[2]后面一个int距离的内存地址)。

 

 

 

使用typedef进行简化:

之前有说过,typedef可以用一个别名替代原名:

例如: typedef 原名 别名;

在某些情况下,可以极大的简化输入。

 

例如上面,我们要表示一个函数指针,需要例如:

int *(*函数指针)(void); 这样

 

如果使用typedef,则可以简化,如:

typedef  int*(*abc)(void);

 

这个时候,abc就表示是函数指针,例如abc  x_1,表示,x_1是一个函数指针。

 

如代码:

#include<iostream>
using namespace std;

typedef int *(*abc)(void);	//使用abc,让abc表示函数指针
//另,好像不能用typedef的别名来代替函数原型和函数头

int* a_1(void);	//a_1函数

int main()
{
	abc m1 = a_1;	//m1指向函数a_1
	cout << *m1() << endl;	//m1()调用函数a_1(得到返回值,是一个指针),加*则解除指针,输出值

	system("pause");
	return 0;
}

int* a_1(void)
{
	int a = 10;
	int *b = &a;
	return b;
}

输出:

10
请按任意键继续. . .

总结:

①个人推测:假如使用typedef,将例如abc之类的代码,放在原本应该是这个类型的名字(函数名、字符串名、变量名等)之类的位置。那么这个例如是abc的代码,就是该类型的别名,

使用方法是:abc之类的代码 def之类名字

就是指def是abc所表示的类型。

 

如上面代码中,abc表示是函数指针的别名,则abc m1也是函数指针。

 

②指针数组也可以用typedef来使用别名。例如代码:

typedef int *(*abc[2])(void);
//使用abc来作为此类指针数组的别名

abc m = { a_1,a_2 };
//m这个指针数组分别指向函数a_1和函数a_2

时间: 2024-09-08 08:09:15

(九十九)函数指针的相关文章

C++基础入门教程(九):函数指针之回调_C 语言

在Java,要实现某个时间调用某段代码,是很简单的事情,那就是使用接口. 而在C++里,有一个比较高深的方式,那就是使用函数指针. 比如Cocos2d-x的定时器(schedule).消息订阅(NotificationCenter)都使用了函数指针来完成回调的功能. 这也是为什么我们总是能把某个函数作为参数传进去,然后在某个时刻这个函数会被调用. 一.函数的地址 要获取一个int变量的地址很简单,比如int num; 那么num的地址就是&num. 而获取函数的地址更简单,函数的名字就是函数的地

mp3-c语言 函数指针编译出错

问题描述 c语言 函数指针编译出错 #ifndef LIBAO_H #define LIBAO_H typedef enum{wav,wim,mp3} io_type_t; typedef struct IOINFO{ io_type_t type; //文件类型编号 char *name; //wav wim,mp3等 char *author; //作者 char *time; //编写时间 char *describe; //模块描述 } io_info_t; typedef struct

编程算法:求1+2+...+n(函数指针) 代码(C++)

题目: 求1+2+...+n, 要求不能使用乘除法\for\while\if\else\switch\case等关键字及条件判断语句(A?B:C). 可以使用函数指针求解, 通过递归调用, 每次递归值减1, 使用求反运算(!), 即非0为0, 0为1. 代码: /* * main.cpp * * Created on: 2014.7.12 *本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/sjjg/ * Aut

谈函数指针(全局/类成员函数)和函数对象

函数指针(全局函数/类成员函数).函数对象(Function object) 一. 函数指针类型为全局函数. #include "stdafx.h"#include <iostream>using namespace std;class TestAction;typedef void (*fp)(int); void Drink(int i){ cout<<"No. "<<i<<" drink..."

声明函数指针并实现回调

程序员常常需要实现回调.本文将讨论函数指针的基本原则并说明如何使用函数指针实现回调.注意这里针对的是普通的函数,不包括完全依赖于不同语法和语义规则的类成员函数(类成员指针将在另文中讨论). 声明函数指针 回调函数是一个程序员不能显式调用的函数:通过将回调函数的地址传给调用者从而实现调用.要实现回调,必须首先定义函数指针.尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似.请看下面的例子: void f():// 函数原型 上面的语句声明了一个函数

C/C++中函数指针的含义

函数存放在内存的代码区域内,它们同样有地址,我们如何能获得函数的地址呢? 如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址. 定义一个指向函数的指针用如下的形式,以上面的test()为例: int (*fp)(int a);//这里就定义了一个指向函数的指针 函数指针不能绝对不能指向不同类型,或者是带不同形参的函数,在定义函数指针的时候我们很容易犯如下的错误. int *fp(int a);//这里是错误的,因为按

C++指针探讨(三)成员函数指针

C语言的指针相当的灵活方便,但也相当容易出错.许多C语言初学者,甚至C语言老鸟都很容易栽倒在C语言的指针下.但不可否认的是,指针在C语言中的位置极其重要,也许可以偏激一点的来说:没有指针的C程序不是真正的C程序. 然而C++的指针却常常给我一种束手束脚的感觉.C++比C语言有更严格的静态类型,更加强调类型安全,强调编译时检查.因此,对于C语言中最容易错用的指针,更是不能放过:C++的指针被分成数据指针,数据成员指针,函数指针,成员函数指针,而且不能随便相互转换.而且这些指针的声明格式都不一样:

C++指针探讨(二)函数指针

在C/C++中,数据指针是最直接,也最常用的,因此,理解起来也比较容易.而函数指针,作为运行时动态调用(比如回调函数 CallBack Function)是一种常见的,而且是很好用的手段. 我们先简单的说一下函数指针.(这一部份没什么价值,纯是为了引出下一节的内容) 2 常规函数指针 void(*fp)(): fp 是一个典型的函数指针,用于指向无参数,无返回值的函数. void(*fp2)(int): fp2 也是一个函数指针,用于指向有一个整型参数,无返回值的函数. 当然,有经验人士一般都会

C++中的函数指针与函数对象的总结

以下是对C++中的函数指针与函数对象的使用进行了详细的分析介绍,需要的朋友可以参考下   篇一.函数指针函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数. 函数指针的声明方法:数据类型标志符 (指针变量名) (形参列表): 一般函数的声明为: int func ( int x );而一个函数指针的声明方法为:int (*func) (int x);前面的那个(