在c语言中,一个函数老是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址(入口地址),因此函数名跟数组名很相似,都是指针常量。node
函数指针就是指向这个入口地址的指针变量,注意函数指针是一个变量。数组
#include<stdio.h> void f(int); int main() { //定义函数指针pf并给pf赋值使其指向函数f的入口地址 //pf先跟*结合,说明pf是一个指针,而后与括号结合,说明这个指针指向函数 void (*pf)(int)=f; //等价于void (*pf)(int)=&f; pf(1); (*pf)(2);//把函数指针转换成函数名,这个转换并不须要 f(3); return 0; } void f(int a) { printf("%d\n",a); }
void (*pf)(int)=&f;为何咱们能够这样定义函数指针呢?来自《c和指针》给出了这样的解释:函数名被使用时老是由编译器把它转换为函数指针,&操做符只是显示地说明了编译器将隐式执行的任务 。函数
c语言协会按期集中讨论一次,每次讨论有一个主持者,每一个主持者对应一个函数(函数功能能够是输出主持者姓名及讨论主题或者完成其余功能)。如今要编写这样一段程序,输入一个整数i(i>=0),根据输入的i调用不一样主持者的函数。很快就能写出以下代码:测试
#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("我是木子"); }
这段代码有错误吗,确定木有,运行结果彻底正确。可是注意这里只列出了5种状况,若是总共有不少种状况呢,那么咱们就要写一大堆的case语句。并且每次都是从case 1 开始判断。那么是否能够简化代码而且能让程序不作这么多判断呢?这就引出了函数指针数组,顾名思义,就是存放函数指针的数组。现主函数修改以下所示:spa
void main() { int i; void (*p[])()={Touch,DuanJiong,MeiKai,YinJun,JiangHaiLong}; scanf("%d",&i); p[i](); }
void (*p[])()={Touch,DuanJiong,MeiKai,YinJun,JiangHaiLong};声明了一个函数指针数组并赋值。把每一个函数的入口地址存入这个数组,这样就不须要用switch语句了,根据下标i直接找到函数入口,省去了判断的时间。.net
什么是回调函数,来着百度百科的解释:回调函数就是一个经过函数指针调用的函数。若是你把函数的指针(地址)做为参数传递给另外一个函数,当这个指针被用为调用它所指向的函数时,咱们就说这是回调函数。这里函数指针是做为参数传递给另外一个函数。设计
你们都写过冒泡排序吧,其代码以下:指针
//冒泡排序 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]; } } }
请注意到这样一个不足,这个冒泡排序只能对int型数组进行排序。若是咱们想写这样一个函数,能同时对int型、float型、double型、char型、结构体类型...数组进行排序,该怎么写呢?也许你会想到函数重载,可是C语言没有这个概念。这里能够用函数指针来实现,其代码比重载更简洁,更高效这也是函数指针的最大用处,参考代码:code
//回调函数对多种数据类型数组进行冒泡排序 //a表示待排序数组 //n表示数组长度 //size表示数组元素大小(即每一个数组元素占用的字节数) //int (*compare)(void *,void *) 声明了一个函数指针,在此做为参数 //void *类型的指针表示指向未知类型的指针,编译器并不会给void类型的指针分配空间,但咱们能够把它进行强制类型转换 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,q都是字符类型的指针,加一都只移动一个字节 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++; } } } }
请注意代码中红色部分代码,要看懂这段代码需明确两个问题:(1)void*类型的指针未分配空间的,咱们能够把它进行强制类型转换成char*。(2)对数组元素进行交换时,并非一次就把两个数交换了,由于咱们并不知道数据的确切类型。但知道数组元素的大小,这样就能够逐个字节进行交换。好比对int类型(占用四个字节)的值a、b进行交换,先交换a、b的第一个字节,而后第二个字节...blog
理解了这个代码,该怎么用呢?参数要传入一个函数指针,因而必需要写一个比较两个数大小的函数,且函数原型必须与int (*compare)(void *,void *)相匹配。下面是测试各类类型数组排序的代码:
#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 *)); //比较两个char类型的数据的大小,a>b返回1,a<b返回-1,a==b返回0 int charCompare(void *a,void *b) { if(*(char*)a==*(char*)b) return 0; return *(char*)a>*(char*)b?1:-1; } //比较两个int类型的数据的大小 int intCompare(void *a,void *b) { if(*(int*)a==*(int*)b) return 0; return *(int*)a>*(int*)b?1:-1; } //比较两个float类型的数据的大小 int floatCompare(void *a,void *b) { if(*(float*)a==*(float*)b) return 0; return *(float*)a>*(float*)b?1:-1; } //比较两个double类型的数据的大小 int doubleCompare(void *a,void *b) { if(*(double*)a==*(double*)b) return 0; return *(double*)a>*(double*)b?1:-1; } //比较两个结构体类型(Node)的数据的大小 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(""); } //回调函数对多种数据类型数组进行冒泡排序 //a表示待排序数组 //n表示数组长度 //size表示数组元素大小(即每一个数组元素占用的字节数) //int (*compare)(void *,void *) 声明了一个函数指针,在此做为参数 //void *类型的指针表示指向未知类型的指针,编译器并不会给void类型的指针分配空间,但咱们能够把它进行强制类型转换 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,q都是字符类型的指针,加一都只移动一个字节 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++; } } } }
运行结果:
再看看C语言标准库中的快速排序函数,它的实现原理及用法同上述冒泡排序
#include<stdlib.h> #include<stdio.h> //建立长度为n的动态数组 //这是一个指针函数 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);//注意a不用时要free掉,不然内存泄露 }
《C和指针》、《the c programming language》、《c语言程序设计》谭浩强版、c标准库、《冒泡排序 && 快速排序 》 、其它网上资料