2015年5月11日 星期一

const vs. #define

大家都知道 Arduino 可用的記憶體非常少,以 UNO 來說它能用來存放變數的靜態記憶體只有 2k,所以在避免超限使用記憶體的情況下,使用 const 或 #define 哪一個會比較節省記憶體呢?


#define 是甚麼?

#define 經常被誤以為是一個程式函式,其實它不是。事實上,它的作用只是會在程式被編譯前將文字內容相互替換而已。例如我們編寫 define.ino 程式碼,如下:

#define PIN 13
void setup() {
  pinMode(PIN, OUTPUT);
}
void loop() {
  digitalWrite(PIN, HIGH);
  delay(500);
  digitalWrite(PIN, LOW);
  delay(500);
}

在 IDE 將程式碼送到編譯器編譯之前,預置處理器會先做一個簡單的內容置換,將 PIN 置換為 13,所以其實編譯器編譯的內容會是如下程式碼:

#define PIN 13
void setup() {
  pinMode(13, OUTPUT);
}
void loop() {
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(500);
}

上述是一個強大且實用的功能,沒有任何記憶體被 "PIN" 這個變數所耗損。事實上,所有程式碼當中並不存在一個叫作 "PIN" 的變數。


const 是甚麼?

關鍵字 "const" 告訴編譯器它是一個不能被編譯的變數(或指標)。然而,它仍舊是一個變數,你要如何使用它,就看你在程式碼當中的使用方式了,或許會也或許不會因而耗損到記憶體。事實上,編譯器藉由使用 IDE 和 avr-gcc 已經聰明到可以辨識具有常數修飾子(const modifier)的變數是不能在程式運作時去改變它的值的,並且它也會試著去避免使用到記憶體。

例如,我們編寫 const.ino 程式碼,如下:

const int pin=13;
void setup() {
  pinMode(pin, OUTPUT);
}
void loop() {
  digitalWrite(pin, HIGH);
  delay(500);
  digitalWrite(pin, LOW);
  delay(500);
}

它將不會耗損任何記憶體,編譯器知道它沒有理由可以在記憶體內建立一個變數,所以原本的記憶體空間還是保存著。


使用 avr-size

您可能已經注意到,在 Arduino 的 IDE 編譯程式碼時,狀態窗口會顯示如下這樣的訊息:


這個"草稿碼二進位的大小"訊息指的是經過編譯後的十六進位檔案(.hex)佔用多少快閃(Flash)記憶體的大小,請注意,它並未指出這個程式碼使用了多少靜態記憶體。

那麼要如何才能知道使用了多少靜態記憶體呢? 我們可以使用 avr-gcc 工具箱裏面的 avr-size.exe (它的路徑是在 <Arduino>\hardware\tools\avr\bin) 去查詢 .elf 檔案 (它的路徑是在 C:\Documents and Settings\USER\Local Settings\Temp\build<一串數字>.tmp 資料夾裏)。

因為我們要比較 define.ino 和 const.ino 各自使用多少靜態記憶體,所以您應該分別編譯這兩個檔案。

由於待會兒我們要在命令視窗下執行,為了方便起見,您可以將 avr-size.exe、define.cpp.elf 和 const.cpp.elf 複製貼到 D:\ 裏面。

現在請開啟命令視窗,並切換路徑到 D:\,然後鍵入

avr-size -C define.cpp.elf

然後您可以看到它顯示如下畫面:


接著再鍵入

avr-size -C const.cpp.elf

然後您可以看到它顯示如下畫面:


結果~~~~讓人非常驚訝對吧? 它們所耗用的記憶體竟然是相同的,都是 9 bytes。


那麼到底使用哪一個比較好?

筆者建議您使用 const 的方式會比較好。因為如果您不小心把 #define PIN 13 寫成 #defun pin 13 (注意 PIN 變成小寫的 pin),如下:

#define pin 13
void setup() {
  pinMode(pin, OUTPUT);
}
void loop() {
  digitalWrite(pin, HIGH);
  delay(500);
  digitalWrite(pin, LOW);
  delay(500);
}

或是不小心把 #define PIN 13 寫成 #defun PIN 13; (注意在13的後面多了一個分號),如下:

#define pin 13;
void setup() {
  pinMode(pin, OUTPUT);
}
void loop() {
  digitalWrite(pin, HIGH);
  delay(500);
  digitalWrite(pin, LOW);
  delay(500);
}

那麼在編譯時將會產生錯誤,而這個錯誤是不太容易發現是在哪兒出錯的,如下圖:




相關文章

有關於 const vs. #define 的社群討論 http://forum.arduino.cc/index.php?topic=86800.15

hex 檔 http://pizgchen.blogspot.tw/2015/04/hex.html

沒有留言:

張貼留言