2013年10月30日 星期三

C Note-Function Pointer(1)

最近開始要k一點系統程式,面試的時候都避免不了被問到有關系統程式的題目QQ

在看系統程式之前,目前覺得有個很重要的觀念一直沒有去弄懂,那就是callback function到底是什麼?從網路搜尋中找到了一個很好的解釋!

Callback = Call back = Call me back when you have done the job = 回電﹝回個電話﹞ = 做好了請叫我一下

這句話完全道盡一切callback function的精神,其實callback精神很常會出現在multi-thread的scenario,例如常常在寫iOS的app,裡面經常會遇到需要async的向server請求資料,當button按下去的時候,app就會啟動一個thread去向server請求資料,而我們可以繼續做想做的事情,當獲得server的資料後,代表"工作完成",然後用delegate的方式去通知目前的main thread!在iOS常用的delegate來達到callback的目的,既然如此,就來看看非物件導向要怎麼辦到callback!

function pointer:

在網路上查了一下宣告function pointer的方式如下:
(*指標變數名)(參數)

在這裡需要很清楚的分辨"*"是放在括號內還是直接接function

int *function_1(int a);    //指標函數
int (*function_2)(int a);  //函數指標

上面的註解還蠻繞口的,function_1為指標函數,就是"回傳"的值為一個"指標",而function_2為函數指標,代表"指向函數"的指標,差別在一個是回傳指標,一個是本身就是指標,反正指標就是用來指向記憶體的位置,所以function pointer一樣不例外,他是指向function記憶體的位置,因為function在程式執行的時候會被放在某個記憶區塊,function pointer就是用來"動態"指向開記憶區塊然後使用該function。

example:

#include <stdio.h>

void printKer(void){

   printf("Hello Ker\n");
}
void printTwoInputNum(int a, int b){ 

   printf("Hello (%d, %d)\n",a,b);
}

int main(int argc, char** argv){

   int a = 10; 
   int b = 20; 
   void (*funKerPtr)();
   void (*funPrintTwoInputNum)(int a,int b); 
   funKerPtr = printKer;
   funPrintTwoInputNum = printTwoInputNum;
   (*funKerPtr)();
   (*funPrintTwoInputNum)(a,b);

   return 0;
}

上面的example實在是有點智障,直接call function不就好了,幹嘛用function pointer,這個example把function pointer用的很爛,雖然function pointer可以這樣用,但有種殺雞焉用牛刀的感覺,什麼時候非用function pointer不可?那就是callback的時候了,下面example是系統程式會看到的一個signal example

example:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>

void signalHandle(int signal){

   switch(signal){
   
      case SIGHUP:
         printf("Catch Signal: SIGHUP(%d)\n",signal);
         break;

      case SIGINT:
         printf("Catch Signal: SIGINT(%d)\n",signal);
         break;
   
      case SIGQUIT:
         printf("Catch Signal: SIGQUIT(%d)\n",signal);
         break;
   
      case SIGALRM:
         printf("Catch Signal: SIGALRM(%d)\n",signal);
         break;
      default:
         printf("Unknown Signal:%d\n",signal);
         break;
   }

}

int main(int argc, char **argv){

   int secDelay = 5;

   printf("current process ID = %d\n",(int)getpid());
   
   signal(SIGINT,signalHandle);
   signal(SIGQUIT,signalHandle);
   signal(SIGALRM,signalHandle);
   alarm(secDelay);

   while(1) pause();

   return 0;
}

signal在系統程式常常拿來做process之間的通信,在上面的example只是一個接收ctrl+/ or ctrl+c或等五秒會跑出一個signal的example,可以看到我們call signal後,unix like的系統會在背景偵測接收到的signal,而signal()函數把signalHandle函數當成input?其實第二個參數是一個function pointer,代表指向signalHandle的一個pointer,所以才可以這樣寫,signal函數在call完之後會在背景async的方式執行,他的工作就是偵測"signal",所以當它偵測到signal的時候(工作做完),這時要告訴我們(callback)的main function他做完惹,這時的做法就是要透過一個function來通知,而這個function為一個callback的功用,因此signalHandle為signal的一個callback function。

可以看到我們可以對callback function做一些客製化的動作,例如更改print出來的句子之類的,所以callback的另外一個好處就是我們可以在某function工作完之後做一些客製化的動作,而不需要去了解到底該function怎麼工作的。

後記:
之前上計程的時候老師沒有強調function pointer和callback概念的重要,老師只說這不會考!結果最近才知道這個概念超重要,整個callback都建立在這個觀念上。現在流行很多的framework都會用到此概念,為什麼這個概念重要,個人認為framework與傳統的library有點不一樣的地方在library都是一堆function,當需要哪些function的時候,直接call他就可以,而framework則不是,它是"一套"規則,framework最常幹的事情就是把實作給"包"起來不讓你看,但是怎麼把執行後的結果傳出來給我們看,或者我們叫這個framework做事怎麼知道他做完沒?因此framework常常會用delegate(物件導向)或是callback的方式來通知我們或是讓我們有最低限度的自定項,老實說我覺得framework都有他的精神所在,本質上framework是希望幫程式設計師解決掉一些麻煩的事,例如access database時免去下sql語法之類的,讓程式設計師可以專注在開發上,但是設計到好用的哲學與概念通常不是那麼簡單,需要花心思去學,好的framework在學完之後會發現真的省了很多事,而這些好用的framework通常都會搭配callback的概念。

reference:

  1. 指標函數和函數指標

沒有留言:

張貼留言