###### tags: `CPP` `Arduino` # 06 使用常數避免誤改變數內容 在前一個教學中, 因為每一次亮度變化的間隔是內層迴圈的圈數 100 乘上每一次亮/熄的總時長 100us, 只有 10ms, 呈現的呼吸燈效果有點急促, 如果希望讓節奏趨緩一些, 可以增加內層迴圈的圈數, 增加亮/熄切換的次數。要做到這一點, 就必須修改兩個內層迴圈的判斷條件, 為了避免漏改, 我們可以**改用一個變數來代表圈數**, 需要調整圈數時, 就只需要更改定義變數的值就可以了, 像是這樣: ```cpp= int total = 100; // 亮/熄切換總次數 void setup() { pinMode(5, OUTPUT); } void loop() { for(int duration_on = 0; duration_on <= 100; duration_on++) { for(int count = 0;count < total; count++) { digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); } } for(int duration_on = 100; duration_on >= 0; duration_on--) { for(int count = 0;count < total; count++) { digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); } } } ``` 在第 1 行我們就建立了一個變數 `total`, 用它來表示內層迴圈要重複的次數, 並且在第 9 和 17 行的判斷條件都改用這個變數。現在我們只要改變 `total` 的值, 就可以變化呼吸燈的效果, 你可以試試看把它改為 200, 和之前的效果比較, 是不是整個韻律緩和多了呢? ## 使用 const 定義常數 `total` 這個變數在定義好值之後, 其實就不會也不應該再改變, 對於這樣的變數, C++ 提供有 [**`const`**](https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/const/) 機制, 可以避免不小心修改它的值, 我們可以把程式改寫為這樣: ```cpp= const int total = 200; // 亮/熄切換總次數 void setup() { pinMode(5, OUTPUT); } void loop() { for(int duration_on = 0; duration_on <= 100; duration_on++) { for(int count = 0;count < total; count++) { digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); } } for(int duration_on = 100; duration_on >= 0; duration_on--) { for(int count = 0;count < total; count++) { digitalWrite(5, LOW); delayMicroseconds(duration_on); digitalWrite(5, HIGH); delayMicroseconds(100 - duration_on); } } } ``` 在第 1 行建立變數時加上 `const`, 就表示這個變數不能被修改, 如果你在程式中重新設定它的值, Arduino IDE 就會發出如下的錯誤訊息, 不會上傳程式: ``` error: assignment of read-only variable 'total' total = 300; ^~~ exit status 1 assignment of read-only variable 'total' ``` 這種在程式中不會再變動的變數, 就稱為**常數 (constant)**。以下我們使用同樣的方式, 把 LED 的腳位編號以及單次亮/熄時長都改成常數: ```cpp= const int total = 200; // 亮/熄切換總次數 const int duration = 100; // 單次亮/熄時長 (us) const int led_pin = 5; // 內建 LED 腳位 void setup() { pinMode(led_pin, OUTPUT); } void loop() { for(int duration_on = 0; duration_on <= duration; duration_on++) { for(int count = 0;count < total; count++) { digitalWrite(led_pin, LOW); delayMicroseconds(duration_on); digitalWrite(led_pin, HIGH); delayMicroseconds(duration - duration_on); } } for(int duration_on = duration; duration_on >= 0; duration_on--) { for(int count = 0;count < total; count++) { digitalWrite(led_pin, LOW); delayMicroseconds(duration_on); digitalWrite(led_pin, HIGH); delayMicroseconds(duration - duration_on); } } } ``` 使用常數除了可以統一管理會用在多處的數值, 避免不小心改動到數值外, 如果幫常數取一個具有意義的名字, 也可以提升程式的可閱讀性, 比單純看到一個數值容易理解多了。 ## 使用 #define 定義不變的數值 你也可以使用 [`#define`](https://www.arduino.cc/reference/en/language/structure/further-syntax/define/) **前置處理器 (preprocessor)** 指令來定義程式中不會變的數值, 例如把剛剛的範例改成這樣: ```cpp #define TOTAL 200 // 亮/熄切換總次數 ... void loop() { for(int duration_on = 0; duration_on <= duration; duration_on++) { for(int count = 0;count < TOTAL; count++) { ... } } for(int duration_on = duration; duration_on >= 0; duration_on--) { for(int count = 0;count < TOTAL; count++) { ... } } } ``` 實際上在處理你的程式時, Arduino 會先進行一道前置處理的程序, 它會先檢視一次程式內容, 然後將出現 `#define` 後面定義名字的地方, 都換成 `#define` 最後面的數值, 最後的程式就跟直接把 200 寫在出現 `TOTAL` 的地方一樣。 使用 `#define` 有幾個地方需要注意: 1. `#define` 是前置處理器的指令, 不是 C++ 的敘述, 不需要以英文分號結尾。 2. 為了和 C++ 語言的內容區分, 用 `#define` 定義的名稱慣例上會全部大寫。 3. `#define` 定義的名稱實際上不存在程式中, 因為在前置處理階段就已經被置換掉了。