C语言学习笔记---指向函数的指针

x33g5p2x  于2021-11-09 转载在 其他  
字(2.7k)|赞(0)|评价(0)|浏览(251)

在内存中函数的存放也是一段连续的内存,函数名就是指向改内存中的首地址,所以也可以将这个函数的首地址赋给一个指针变量,这样通过指针变量就可以访问改函数。
  那么为什么要通过指针来访问函数呢?下面通过一个简单的例子来演示一下。

int (*fun)(int m,int n);

int Add(int x,int y)
{
	return x + y;
}
int Sub(int x,int y)
{
	return x - y;
}
int Mul(int x,int y)
{
	return x * y;
}
int Div(int x,int y)
{
	return x / y;
}

int main()
{
	int ret = 0;

	int i = 0;

	for(i=0; i<4; i++)
		{

			switch(i)
				{
					case 0:
						fun = Add;
						break;
					case 1:
						fun = Sub;
						break;
					case 2:
						fun = Mul;
						break;
					case 3:
						fun = Div;
						break;
				}

			ret = fun(10,5);

			printf("%d\r\n",ret);
		}
	system("pause");
	return 0;
}

定义了加、减、乘、除四个函数,在for循环中依次将这四个函数传递给函数指针,分别计算两个整数的加、减、乘、除的结果。代码执行结果如下:

  看到这里就会有个疑问?为什么非要用函数指针来实现呢?直接在switch语句中调用不同的函数不是依然能实现这个过程吗?对于这个例子来讲,是没有必要非要用指针啦传递函数。完全可以直接调用具体函数来实现。

上面的例子中由于所有函数的功能都是自己实现的,所以直接调用和通过指针调用没有多大的区别,但是在很多情况下,函数需要用户自己去实现,那么此时使用指针的优势就比较明显了。比如下面的例子:

int values[] = { 88, 56, 100, 2, 25 };

int up (const void * a, const void * b)
{
	return ( *(int*)a - *(int*)b );
}

int down (const void * a, const void * b)
{
	return ( *(int*)b - *(int*)a );
}

int main()
{
	int n;

	puts("排序之前的列表:");
	for( n = 0 ; n < 5; n++ )
		{
			printf("%d ", values[n]);
		}

	puts("\n升序排列");
	qsort(values, 5, sizeof(int), up);

	puts("排序之后的列表:");
	for( n = 0 ; n < 5; n++ )
		{
			printf("%d ", values[n]);
		}
	puts("\n降序排列");
	qsort(values, 5, sizeof(int), down);

	puts("排序之后的列表:");
	for( n = 0 ; n < 5; n++ )
		{
			printf("%d ", values[n]);
		}
	system("pause");
	return 0;
}

这里使用了C库中的排序函数,函数原型如下:

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))
参数:
	base -- 指向要排序的数组的第一个元素的指针。
	nitems -- 由 base 指向的数组中元素的个数。
	size -- 数组中每个元素的大小,以字节为单位。
	compar -- 用来比较两个元素的函数。

这里最后一个比较函数就需要用户自己去实现,根据用户的提供的函数,排序函数就可以自动的去选择是升序排列还是降序排列。如果给qsort()函数里面传递的是up()函数,那么数组中的数据就会升序排列,如果传递的是down()函数,那么数组中的数据就降序排列。

  在这个例子中qsort()函数是C库函数,这个函数的最后一个参数也是一个函数,这个函数必须由用户来提供。那么此时用指针来传递函数是最方便的,因为用指针传递函数的时候,函数名可以由用户自己定义。这样qsort()函数的通用性就会非常好了。如果要传递的函数名称和参数都必须是固定的话,在使用起来就有很大的局限性。

所以使用指向函数的指针来传递参数时,可以更好的封装函数,提高函数的通用性和易读性。

下面详细的说一下,如何将一个函数替换为指针。
首先看一个函数原型:
int Add(int x,int y);

Add()函数类型是 “带int int类型参数,返回类型是int型的函数”,将它改写为指针pf指向该函数类型:

int (*pf)(int x,int y);

替换的方式为 将函数名改为 ( * pf) ,其他部分保持不变。这样就将函数替换为指向指针的函数了,这里一定要加上小括号,如果不加小括号的话就会变成

int *pf(int x,int y);

由于小括号的优先级高,所以pf就会先和后面的小括号结合,变成 pf(int x,int y),函数的返回类型就变成了 int * ,这样含义完全就变了,变成了 函数pf()有两个参数,它的返回值为 int 指针类型。所以这里一定要加上小括号。

其中参数列表中可以会将变量省略,只写变量的类型。上面指向函数的指针可以简写为:
int (*pf)(int ,int );

由于函数名指向了内存中函数的起始地址,所以要将函数的地址可以直接使用下面的方法:

pf = Add;

将函数Add的首地址直接赋值给指针pf,这样通过指针pf就可以访问Add()函数了。
所以上面的函数 Add(x,y) 可以直接替换为 (*pf)(x,y) ,相当于 Add == (*pf)。但是通过上面第一个例子可以发现,使用 pf(x,y)调用函数的时候也是可以正常使用的。虽然 (*pf)(x,y) 和 pf(x,y)这两种调用方式看起来是矛盾的,但是C语言中对于函数指针来说这两种用法都可以,是一样的。

fun = Add;
	ret = fun(10,5);
	printf("%d\r\n",ret);

	fun = Sub;
	ret = (*fun)(10,5);
	printf("%d\r\n",ret);

通过这两种方式分别调用函数,输出结果如下:

  如果要更复杂一点,可以声明一个指针数组,依次存储这4个函数。

typedef int (*fp)(int int);
fp fun[4] = {Add,Sub,Mul,Div};

通过一个函数指针数组可以更方便的调用各个函数。

相关文章

微信公众号

最新文章

更多