在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標準庫、《冒泡排序 && 快速排序 》 、其它網上資料