函數指針

 函數指針是指向函數的指針變量。 因而“函數指針”本身首先應是指針變量,只不過該指針變量指向函數。這正如用指針變量可指向整型變量、字符型、數組一樣,這裏是指向函數。如前所述,C在編譯時,每一個函數都有一個入口地址,該入口地址就是函數指針所指向的地址。有了指向函數的指針變量後,可用該指針變量調用函數,就如同用指針變量可引用其他類型變量一樣,在這些概念上是一致的。函數指針有兩個用途:調用函數和做函數的參數

    函數指針的聲明方法爲:

    數據類型標誌符 (指針變量名) (形參列表);

    注1:“函數類型”說明函數的返回類型,由於“()”的優先級高於“*”,所以指針變量名外的括號必不可少,後面的“形參列表”表示指針變量指向的函數所帶的參數列表。例如:

 int func(int x);   /* 聲明一個函數 */  

    int (*f) (int x);    /* 聲明一個函數指針 */  

    f=func;   /* 將func函數的首地址賦給指針f */  

    賦值時函數func不帶括號,也不帶參數,由於func代表函數的首地址,因此經過賦值以後,指針f就指向函數func(x)的代碼的首地址。  

    注2:函數括號中的形參可有可無,視情況而定。   

    下面的程序說明了函數指針調用函數的方法:   

#include<stdio.h>
int max(int x,int y){ return(x>y?x:y); }
void main()
{
   int (*ptr)(int, int);
  int a,b,c;
  ptr=max;
  scanf("%d,%d",&a,&b);
  c=(*ptr)(a,b);
  printf("a=%d,b=%d,max=%d",a,b,c);
} 

     ptr是指向函數的指針變量,所以可把函數max()賦給ptr作爲ptr的值,即把max()的入口地址賦給ptr,以後就可以用ptr來調用該函數,實際上ptr和max都指向同一個入口地址,不同就是ptr是一個指針變量,不像函數名稱那樣是死的,它可以指向任何函數,就看你想怎麼做了。在程序中把哪個函數的地址賦給它,它就指向哪個函數。而後用指針變量調用它,因此可以先後指向不同的函數。不過注意,指向函數的指針變量沒有++和--運算,用時要小心。   

     不過,在某些編譯器中這是不能通過的。這個例子的補充如下。應該是這樣的:   

     1.定義函數指針類型: typedef int (*fun_ptr)(int,int);   

     2.申明變量,賦值:  fun_ptr max_func=max; 也就是說,賦給函數指針的函數應該和函數指針所指的函數原型是一致的。  

#include<stdio.h>
void FileFunc()
{
   printf("FileFunc\n");
}
void EditFunc()
{
  printf("EditFunc\n");
}
void main()
{
  typedef void (*funcp)();
  funcp pfun= FileFunc;
  pfun();
  pfun = EditFunc;
  pfun(); 
}


1. 首先,在C語言中函數是一種function-to-pointer的方式,即對於一個函數,會將其自動轉換成指針的類型.如:

#include<stdio.h>
void fun()
{
}
int main()
{
   printf("%p      %p      %p\n", &fun, fun, *fun);
   return 0;
}
這三個值的結果是一樣的. 其實對於最後的那個*fun, 即使前面加上很多個*號, 其結果也不變, 即**fun, ***fun的結果都是一樣的. 對於這個問題, 因爲之前講過函數是一種
function-to-pointer方式, 其會自動轉換成指針的類型, &fun是該函數的地址, 爲指針類型, fun是一個函數, 會轉換成其指針類型, 而對於*fun, 由於fun已經變成了指針類型, 指向這個函數, 所以*fun就是取這個地址的函數, 而又根據function-to-pointer, 該函數也轉變成了一個指針, 所以以此類推, 這三個值的結果是相同的.

2. 如何調用一個地址上的函數
    如果知道了一個函數所在的地址, 可以將其強制轉化成某一種類型的函數指針, 然後再根據這個指針去調用這個地址的函數. 如:

#include<stdio.h>
typedef void (*pfun)(int);
void f(int i)
{
   printf("i = %d\n", i);
}
int main()
{
   unsigned long add;
   add = (unsigned long)f;
   ((pfun)add)(10);
   (*(pfun)add)(20);
   return 0;
}
    使用(void (*)(int))的方式可以將一個地址轉換成一個帶int參數且沒有返回值的函數的指針類型, 然後再去調用, 由於第1點中講的function-to-pointer, 所以最後兩條語句中加與不加那個*號效果都是一樣的. 在嵌入式方面經常用到這種方式.

3. 函數指針數組的用法.

   有時候需要定義一個數組, 其內容爲一系列的函數指針, 然後對其進行調用, 如:

#include<stdio.h>
typedef int (*pfun)(int, int);
int max(int v1, int v2)
{
   return (v1 > v2 ? v1 : v2);
}
int min(int v1, int v2)
{
   return (v1 < v2 ? v1 : v2);
}
int sum(int v1, int v2)
{
   return (v1 + v2);
}
int main()
{
   pfun p[3];
   p[0] = max;
   p[1] = min;
   p[2] = sum;
   printf("p[0] = %d\n", (p[0])(3, 5));
   printf("p[1] = %d\n", (p[1])(4, 6));
   printf("p[2] = %d\n", (p[2])(1, 2));
   return 0;
}
   雖然感覺這種方法有點累贅, 但是也算是一種使用的方式, 所以介紹一下.

4.返回一個指向數組的指針的方式

   可以讓函數返回一個指向數組的一個指針, 如:

#include<stdio.h>
#include<stdlib.h>

typedef int (*pfun)[10];
pfun p()
{
   pfun m;
   int i;
   m = (pfun)malloc(10 * sizeof(int));
   if (m == NULL)
   {
      printf("malloc error\n");
      exit(1);
   }
   for (i = 0; i < 10; i++)
      *(*m+i) = i+1;
   return m;
}

int main()
{
   pfun a;
   int i;
   a = p();
   for (i = 0; i < 10; i++)
      printf("%d ", *(*a+i));
   printf("\ndone\n");
   return 0;
}
   這種方式中,int (*a)[10]是一個指向一維數組的一個指針, 而p()也是返回一個指向一維數組的一個指針.

5.返回一個函數指針的指針
   對這個問題, signal()函數是最好的例子

void (*signal (int signo, void (*func)(int)))(int);
   很多朋友剛開始看這個函數定義的時候是不太懂, 其實可以一步一步地慢慢看, 我以前是這樣分析的, 希望能對大家有用.
   int (*p)(); 這是一個函數指針, p所指向的函數是一個不帶任何參數, 並且返回值爲int的一個函數.

   int (*fun())(); 這個式子與上面式子的區別在於用fun()代替了p,而fun()是一個函數,所以說就可以看成是fun()這個函數執行之後,它的返回值是一個函數指針,這個函數指針(其實就是上面的p)所指向的函數是一個不帶任何參數,並且返回值爲int的一個函數.所以說對於

void (*signal(int signo, void (*fun)(int)))(int);
   就可以看成是signal()函數(它自己是帶兩個參數,一個爲整型,一個爲函數指針的函數), 而這個signal()函數的返回值也爲一個函數指針,這個函數指針指向一個帶一個整型參數,並且返回值爲void的一個函數.signal函數返回的其實是指向以前的信號處理程序的指針, 所以舉一個例子來說明返回指向函數的指針的用法,如:

#include<signal.h>
#include<stdlib.h>
#include<stdio.h>

void sig_fun2(int signo)
{
   printf("in sig_fun2:%d\n", signo);
}
void sig_fun1(int signo)
{
   printf("in sig_fun1:%d\n", signo);
}
int main()
{
   unsigned long i;
   if (signal(SIGUSR1, sig_fun1) == SIG_ERR)
   {
      printf("signal fun1 error\n");
      exit(1);
   }

   (signal(SIGUSR1, sig_fun2))(30);
   printf("done\n");
   return 0;
}
6. 使用函數指針作爲參數的情況

    在函數的參數中, 可能會帶有一個函數指針, 這在signal()函數中是出現了的, 另外再寫個例子如:

#include<stdio.h>

typedef int (*pfun)(int, int);
int max(int v1, int v2)
{
   return (v1 > v2 ? v1 : v2);
}
int min(int v1, int v2)
{
   return (v1 < v2 ? v1 : v2);
}
int sum(int v1, int v2)
{
   return (v1 + v2);
}
int fun(int a, int b, pfun call)
{
   return (call(a, b));
}
int main()
{
   printf("max=%d\n", fun(1, 2, max));
   printf("min=%d\n", fun(3, 4, min));
   printf("sum=%d\n", fun(5, 6, sum));
   return 0;
}

   其實在很多排序函數中就是使用的這個參數爲函數指針的方式來進行調用的.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章