###### 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` 定義的名稱實際上不存在程式中, 因為在前置處理階段就已經被置換掉了。