本文转载自http://blog.csdn.net/touch_2011/article/details/6984029
觉得博主写的很好,转载过来学习。
1、函数指针(指向函数的指针)
在C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址(入口地址),所以函数名跟数组名很类似,都是指针常量。
函数指针就是指向这个入口地址的指针变量,注意函数指针是一个变量。
- <span style="font-size:16px;">#include<stdio.h>
-
- void f(int);
-
- int main()
- {
-
-
- void (*pf)(int)=f;
- pf(1);
- (*pf)(2);
- f(3);
- return 0;
- }
-
- void f(int a)
- {
- printf("%d\n",a);
- }
- </span>
void (*pf)(int)=&f;为什么我们可以这样定义函数指针呢?来自《c和指针》给出了这样的解释:函数名被使用时总是由编译器把它转换为函数指针,&操作符只是显示地说明了编译器将隐式执行的任务 。
2、妙用函数指针一: 函数指针数组
c语言协会定期集中讨论一次,每次讨论有一个主持者,每个主持者对应一个函数(函数功能可以是输出主持者姓名及讨论主题或者完成其他功能)。现在要编写这样一段程序,输入一个整数i(i>=0),根据输入的i调用不同主持者的函数。很快就能写出如下代码:
- <span style="font-size:16px;">#include<stdio.h>
-
-
- void Touch();
- void DuanJiong();
- void MeiKai();
- void YinJun();
- void JiangHaiLong();
-
- void main()
- {
- int i;
- scanf("%d",&i);
- switch(i){
- case 0:
- Touch();
- break;
- case 1:
- DuanJiong();
- break;
- case 2:
- MeiKai();
- break;
- case 3:
- YinJun();
- break;
- case 4:
- JiangHaiLong();
- break;
- }
- }
-
- void Touch()
- {
- puts("我是Touch");
- }
-
- void DuanJiong()
- {
- puts("我是段炯");
- }
-
- void MeiKai()
- {
- puts("我是梅凯");
- }
-
- void YinJun()
- {
- puts("我是殷俊");
- }
-
- void JiangHaiLong()
- {
- puts("我是木子");
- }</span>
这段代码有错误吗,肯定木有,运行结果完全正确。但是注意这里只列出了5种情况,如果总共有很多种情况呢,那么我们就要写一大堆的case语句。而且每次都是从case 1 开始判断。那么是否可以简化代码并且能让程序不做这么多判断呢?这就引出了函数指针数组,顾名思义,就是存放函数指针的数组。现主函数修改如下所示:
- <span style="font-size:16px;">void main()
- {
- int i;
- void (*p[])()={Touch,DuanJiong,MeiKai,YinJun,JiangHaiLong};
- scanf("%d",&i);
- p[i]();
- }
-
- void Touch()
- {
- puts("我是Touch");
- }
-
- void DuanJiong()
- {
- puts("我是段炯");
- }
-
- void MeiKai()
- {
- puts("我是梅凯");
- }
-
- void YinJun()
- {
- puts("我是殷俊");
- }
-
- void JiangHaiLong()
- {
- puts("我是木子");
- }</span>
-
- </span>
void (*p[])()={Touch,DuanJiong,MeiKai,YinJun,JiangHaiLong};声明了一个函数指针数组并赋值。把每个函数的入口地址存入这个数组,这样就不需要用switch语句了,根据下标i直接找到函数入口,省去了判断的时间。
3、妙用函数指针二: 回调函数
什么是回调函数,来着百度百科的解释:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。这里函数指针是作为参数传递给另一个函数。
大家都写过冒泡排序吧,其代码如下:
- <span style="font-size:16px;">
- void bubbleSort(int *a,int n)
- {
- int i,j;
- for(i=1;i<n;i++)
- for(j=1;j<n-i+1;j++){
- if(a[j+1]<a[j]){
- a[j]=a[j]+a[j+1];
- a[j+1]=a[j]-a[j+1];
- a[j]=a[j]-a[j+1];
- }
- }
- }</span>
请注意到这样一个不足,这个冒泡排序只能对int型数组进行排序。如果我们想写这样一个函数,能同时对int型、float型、double型、char型、结构体类型...数组进行排序,该怎么写呢?也许你会想到函数重载,但是C语言没有这个概念。这里可以用函数指针来实现,其代码比重载更简洁,更高效这也是函数指针的最大用处,参考代码:
- <span style="font-size:16px;">
-
-
-
-
-
- void bubbleSort(void *a,int n,int size,int (*compare)(void *,void *))
- {
- int i,j,k;
- char *p,*q;
- char temp;
- for(i=0;i<n;i++)
- for(j=0;j<n-i-1;j++){
-
- </span><span style="font-size:16px;"><span style="color:#cc0000;">p=(char*)a+j*size;
- </span> </span><span style="font-size:16px;"><span style="color:#cc0000;">q=(char*)a+(j+1)*size;
- </span> if(compare(p,q)>0){
-
- for(k=0;k<size;k++){
- temp=*p;
- *p=*q;
- *q=temp;
- </span><span style="font-size:16px;"><span style="color:#cc0000;">p++;
- </span> </span><span style="font-size:16px;"><span style="color:#cc0000;">q++;
- </span> }
- }
- }
- }</span>
请注意代码中红色部分代码,要看懂这段代码需明确两个问题:(1)void*类型的指针未分配空间的,我们可以把它进行强制类型转换成char*。(2)对数组元素进行交换时,并不是一次就把两个数交换了,因为我们并不知道数据的确切类型。但知道数组元素的大小,这样就可以逐个字节进行交换。比如对int类型(占用四个字节)的值a、b进行交换,先交换a、b的第一个字节,然后第二个字节...
理解了这个代码,该怎么用呢?参数要传入一个函数指针,于是必须要写一个比较两个数大小的函数,且函数原型必须与int (*compare)(void *,void *)相匹配。下面是测试各种类型数组排序的代码:
- <span style="font-size:16px;">#include<stdio.h>
-
- typedef struct{
- int data;
- }Node;
-
-
- int charCompare(void *a,void *b);
- int intCompare(void *a,void *b);
- int floatCompare(void *a,void *b);
- int doubleCompare(void *a,void *b);
- int nodeCompare(void *a,void *b);
- void bubbleSort(void *a,int n,int size,int (*compare)(void *,void *));
-
-
- int charCompare(void *a,void *b)
- {
- if(*(char*)a==*(char*)b)
- return 0;
- return *(char*)a>*(char*)b?1:-1;
- }
-
- int intCompare(void *a,void *b)
- {
- if(*(int*)a==*(int*)b)
- return 0;
- return *(int*)a>*(int*)b?1:-1;
- }
-
- int floatCompare(void *a,void *b)
- {
- if(*(float*)a==*(float*)b)
- return 0;
- return *(float*)a>*(float*)b?1:-1;
- }
-
- int doubleCompare(void *a,void *b)
- {
- if(*(double*)a==*(double*)b)
- return 0;
- return *(double*)a>*(double*)b?1:-1;
- }
-
- int nodeCompare(void *a,void *b)
- {
- if(((Node*)a)->data == ((Node*)b)->data)
- return 0;
- return ((Node*)a)->data > ((Node*)b)->data ? 1 : -1;
- }
-
- void main()
- {
- int i=0;
-
- char c[]={'d','a','c','e','b'};
- int a[]={3,2,4,0,1};
- float f[]={4.4,5.5,3.3,0,1};
- double b[]={4.4,5.5,3.3,0,1};
- Node n[]={{2},{0},{1},{4},{3}};
-
-
- puts("对char类型数组进行排序:");
- bubbleSort(c,5,sizeof(char),charCompare);
- for(i=0;i<5;i++)
- printf("%c ",c[i]);
- puts("");
-
- puts("对int类型数组进行排序:");
- bubbleSort(a,5,sizeof(int),intCompare);
- for(i=0;i<5;i++)
- printf("%d ",a[i]);
- puts("");
-
- puts("对float类型数组进行排序:");
- bubbleSort(f,5,sizeof(float),floatCompare);
- for(i=0;i<5;i++)
- printf("%.2f ",f[i]);
- puts("");
-
- puts("对double类型数组进行排序:");
- bubbleSort(b,5,sizeof(double),doubleCompare);
- for(i=0;i<5;i++)
- printf("%.2lf ",b[i]);
- puts("");
-
- puts("对结构体(Node)类型数组进行排序:");
- bubbleSort(n,5,sizeof(Node),nodeCompare);
- for(i=0;i<5;i++)
- printf("%d ",n[i].data);
- puts("");
- }
-
-
-
-
-
-
-
-
- void bubbleSort(void *a,int n,int size,int (*compare)(void *,void *))
- {
- int i,j,k;
- char *p,*q;
- char temp;
- for(i=0;i<n;i++)
- for(j=0;j<n-i-1;j++){
-
- p=(char*)a+j*size;
- q=(char*)a+(j+1)*size;
- if(compare(p,q)>0){
-
- for(k=0;k<size;k++){
- temp=*p;
- *p=*q;
- *q=temp;
- p++;
- q++;
- }
- }
- }
- }</span>
运行结果:
再看看C语言标准库中的快速排序函数,它的实现原理及用法同上述冒泡排序
4、指针函数(返回指针的函数,确切的说是返回指针类型的函数)
- <span style="font-size:16px;">#include<stdlib.h>
- #include<stdio.h>
-
-
-
- int* array(int n)
- {
- int *a=(int*)malloc(sizeof(int)*n);
- return a;
- }
- void main()
- {
- int i,n=3;
- int *a=array(n);
- for(i=0;i<n;i++)
- a[i]=i;
- free(a);
- }</span>
5、参考资料
《C和指针》、《the c programming language》、《c语言程序设计》谭浩强版、c标准库、《冒泡排序 && 快速排序 》 、其它网上资料