認識Arduino與C語言的函式指標以及函式指標陣列

讀者詢問如何在Arduino中建立數個自訂函式,並且透過「函式指標陣列」,在不使用if…else或switch…case等條件判斷式的情況下,讓程式依據變數的值,執行不同的自訂函式。

下文將先介紹Arduino與C程式語言的函式指標語法。

建立函式指標程式

就像變數一樣,函式(function)同樣被暫存在某個記憶體區塊,可以透過指標(pointer)取用;指向函式的指標稱為「函式指標(function pointer)」

底下的Arduino程式宣告了一個hello()自訂函式,並透過一個叫做‘f’的指標指向並執行它:

函式指標程式示範

若在Arduino執行此程式,將能在序列埠監控視窗看見“hello world.”訊息。

函式指標的語法

函式指標的語法如下:

函式指標的語法

指標名稱必須用代表「優先處理」的小括號包圍,如果少了小括號,程式敘述的意義將大不相同:

指標名稱必須用代表「優先處理」的小括號包圍

上面的Arduino程式碼,若用C語言改寫,將變成:

#include 

// 宣告自訂函式
void hello() {
  printf("hello world.\n");
}

// 宣告指向hello函式的函式指標
void (*f)() = hello;

void main(void) {
  // 透過函式指標執行hello函式
  (*f)();
}

函式指標的參數傳遞與傳回值

假如要透過指標參照具有輸入參數和傳回值的函式,例如,一個計算並傳回兩整數相加值的自訂函式“sum”:

計算並傳回兩整數相加值的自訂函式“sum”

函式指標的定義也要跟著修改:

具備參數傳遞與傳回值的函式指標

完整的Arduino範例程式如下:

// 宣告自訂函式
int sum(int x, int y) {
  return x + y;
}

// 宣告具有兩個參數以及整數傳回值的函式指標
int (*f)(int x, int y) = sum;

void setup() {
  Serial.begin(9600);
  
  // 執行函式指標並傳遞兩個參數
  int val = (*f)(8, 4);
  
  // 將在序列埠監控視窗顯示"Ans: 12"
  Serial.print("Ans: ");
  Serial.println(val);
}

void loop() {
  // 這裡沒有程式
}

相同功能的C語言程式的範例如下:

#include 

int sum(int x, int y) {
  return x + y;
}

int (*f)(int x, int y) = sum;

void main(void) {
  int val = (*f)(8, 4);
  
  printf("Ans: %d\n", val);
}

函式指標陣列

認識函式指標的語法之後,函式指標陣列的語法也很容易理解。假設程式事先宣告了名叫fn0, fn1和fn3的函式,底下的敘述將透過自訂的 "f" 指標陣列指向它們:

底下是Arduino版本的函式指標陣列範例,上傳程式碼之後,開啟序列埠監控視窗,接著在序列埠監控視窗中輸入0~2的數字,程式將執行對應的fn0~fn2函式。

// 宣告三個自訂函式
void fn0() {
  Serial.println("Hello from fn0.");
}

void fn1() { 
  Serial.println("Hello from fn1.");
}

void fn2() {
  Serial.println("Hello from fn2.");
}

// 建立函式陣列指標
void (*f[3]) () = {fn0,fn1,fn2};

void setup() {
  Serial.begin(9600);
  Serial.println("Please input number 0~2:");
}

void loop() {
  if( Serial.available() ) {
    // 讀取序列輸入值,並轉換成整數。
   byte val = Serial.read() - 48;
   // 確認輸入值介於0和2之間
   if (val >= 0 && val < = 2) {
     // 將val當作陣列索引,執行對應的函式。
      (*f[val])();
   } else {
     // 若輸入值超過指定範圍,則顯示“Wrong number!”(錯誤數字!)
      Serial.println("Wrong number!");
    }
  }
}

底下是C程式語言的版本:

#include 

void fn0() {
  printf("Hello from fn0.\n");
}

void fn1() { 
  printf("Hello from fn1.\n");
}

void fn2() {
  printf("Hello from fn2.\n");
}

void (*f[3]) () = {fn0,fn1,fn2};

void main(void) {
  int val;

  printf("\nPlease input number 0~2:");
  // 讀取輸入值
  scanf("%d",&val);

  if(val>=0 && val< =2) { 
    (*f[val])();
  } else {
    printf("Wrong number!\n");
  }
}

延伸閱讀

Posts created 467

10 thoughts on “認識Arduino與C語言的函式指標以及函式指標陣列

  1. 昨天研究了一下老師的方法, 解決了我的問題, 且我發現更棒的是函式命名不必有數字上順序的關係, 只要函式指標陣列跟函式有對應到, 順序就照陣列使用方式取用, 這樣說是對的吧?
    void fn0() {
    }

    void power_on() {
    }

    void check_status() {

    }

    void (*f[3]) () = {fn0, power_on, check_status};

  2. 感謝老師^_^
    我昨天發現原來我第一本老師的書是Flash 5 撼動網頁寶典, 而非Flash MX 網頁動畫寶典, ^_^

  3. 老師您好:我發現 void (*f[3]) () = {fn0,fn1,fn2}; 這樣宣告了3個函式不僅會佔用3個ROM空間,也多佔用了3個記憶空間(RAM)。
    在MCU 裡,RAM空間是很有限的。
    想請教是否可將{fn0,fn1,fn2} 3個函式”位置”宣告為CONST常數,共用一個指標變數?
    如果有很多的函式放在”陣列”中,也只會多佔用ROM空間 ,而不會佔用RAM空間。謝謝。

    1. 函式並非內容固定不變的常數,執行期間一定會被載入記憶體(堆疊),但執行完畢就從記憶體移除。除非你要執行大量的函式(例如:遞迴),通常不用擔心這個問題。

      thanks,
      jeffrey

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top