之前我们了解了指针,知道如何定义一个指针,接着知道二级指针如何使用。下面我们简单的来回忆一下:

int a = 10;     //一个×××变量
int *p = &a;    //一个指向×××的指针
int **q = &p;   //q是一个指向p的指针,即它是一个指向×××的指针的指针

在这里我们要认识一个新的指针,函数指针,即指向函数的指针。例:

int (*fun)();   //fun是函数指针,它所指向的函数返回值为×××

解析:我们首先要知道这是一个声明,其中这两个花括号也代表了不同的含义。第二个花括号是函数调用操作符,第一个花括号中间接访问操作符迫使间接访问在函数调用之前进行,因此这里使fun成为一个函数指针,它所指向的函数返回值为×××。

int *(*fun)();   //fun是函数指针,它所指向的函数返回值为×××指针

与上述声明不同的是,返回值不同。

int *fun[10];     //一个包含10个元素的指针数组
int (*fun[10])();   //fun为10个元素的一个函数指针数组

如果函数有参数,我们必须把函数的参数加上,使用完整的函数原型,当然声明时可以不写形参变量名。例:

int (*fun)(int , char);
int (*fun[10])(int ,char);

下面我们来讲一讲函数指针的应用吧!

最常见的两个用途是回调函数和转换表。

回调函数:用户把一个函数指针作为参数传递给其他函数,后者将回调用户的函数。

在这里,我们用冒泡排序的方法来实现一下字符串,×××数组的比较吧。

#include<stdio.h>
#include<string.h>

int int_cmp(void *a1, void *a2)   //×××数组的比较
{
	return *(int *)a1-*(int *)a2;
}

int str_cmp(void *s1, void *s2)   //字符串的比较
{
	return strcmp((char *)*(int *)s1,(char *)*(int *)s2);
}

void swap(void *p, void *q, int size)   //将两值交换
{                                       //我们需要交换函数作用于任何类型的值,因此把参数
	int i = 0;                      声明为(void *),一个指向未知类型的指针
	char tmp;
	for(i = 0;i < size;i++)
	{
		 tmp = *((char*)p+i);
		 *((char*)p+i) =*((char*)q+i);
		 *((char*)q+i) = tmp;
	}
}

void bubble(void *ptr, int size , int len ,int(*cmp)(void *p,void *q))  //冒泡排序
{
	int i = 0,j = 0;
	for(i = 0;i < size-1;i++)
	{
		for(j = 0;j <size-1-i;j++)
		{
			if(cmp((char *)ptr+j*len,(char *)ptr+(j+1)*len) > 0)
			{
				swap((char *)ptr+j*len,(char *)ptr+(j+1)*len,len);
			}
		}
	}
}

int main()
{
	int arr[]={1,3,4,5,7,8,9,6,2,10};
	char *str[]= {"ddddd","ccccd","bbbbb","aasas","bbbbd"};
	int i=0 ;
	int size = sizeof(arr)/sizeof(arr[0]);
	int sz = sizeof(str)/sizeof(str[0]);
	bubble(arr, size, sizeof(int), int_cmp);  //回调函数int_cmp
	for(i = 0;i < size;i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
	bubble(str,sz,sizeof(char *),str_cmp);    //回调函数str_cmp
	for(i = 0;i <sz;i++)
	{
		printf("%s\n",str[i]);
	}
	return 0;
}

转移表:在这里,我们可以用转移表来实现一个袖珍式计算器。比如原始的计算器函数可以写为:

#include<stdio.h>
#include<stdlib.h>
enum jl
{
	EXIT,
	ADD,
	SUB,
	MUL,
	DIV
};
void menu()
{
	printf("          1.add     2.sub\n");
	printf("          3.mul     4.div\n");
	printf("          0.exit         \n");
}

int _add(int num1,int num2)
{
	return num1 + num2;
}

int _sub(int num1,int num2)
{
	return num1 - num2;
}

int _mul(int num1,int num2)
{
	return num1 * num2;
}

int _div(int num1,int num2)
{
	return num1 / num2;
}

int main()
{
	int select = 1;
	int ret = 0;
	int num1 = 0,num2 = 0;
	while(select)
	{
		menu();
		printf("请选择:\n");
		scanf("%d",&select);
		if(select != 0)
		{
			printf("请输入两个整数:\n");
			scanf("%d%d",&num1,&num2);
		}	
		switch(select)
		{
			case ADD:
				ret = _add( num1, num2);
				break;
			case SUB:
				ret = _sub( num1, num2);
				break;
			case MUL:
				ret = _mul( num1, num2);
				break;
			case DIV:
				ret = _div( num1, num2);
				break;
			case EXIT:
				exit(1);
				break;
		}
		printf("ret = %d\n",ret );
	}
	return 0;
}


而知道了转移表后,我们可以这样写:

#include<stdio.h>
#include<stdlib.h>
	
void menu()
{
	printf("          1.add     2.sub\n");
	printf("          3.mul     4.div\n");
	printf("          0.exit         \n");
}

int _add(int num1,int num2)
{
	return num1 + num2;
}

int _sub(int num1,int num2)
{
	return num1 - num2;
}

int _mul(int num1,int num2)
{
	return num1 * num2;
}

int _div(int num1,int num2)
{
	return num1 / num2;
}

int main()
{
	int (*fun[5])(int ,int ) = {0, _add, _sub, _mul, _div};
	int select = 1;
	int ret = 0;
	int num1 = 0,num2 = 0;
	while(select)
	{
		menu();
		printf("请选择:\n");
		scanf("%d",&select);
		if(select != 0)
		{
			printf("请输入两个整数:\n");
			scanf("%d%d",&num1,&num2);	
			ret = fun[select](num1,num2);
			printf("ret = %d\n",ret );
		}
	}
	return 0;
}

在这里,转移表就是一个函数指针数组。对于函数指针,大家肯定有了新的认识吧。