###### tags: `CPP` `Arduino`
# 10 利用結構 (struct) 結合相關資料
使用多維陣列雖然已經可以把分散的資料整合在一起, 可是因為陣列是使用序數索引來存取資料, 這個序數和實際存放的資料並沒有實質上的關係, 如果沒有特別標記, 一段時間後你可能忘了前一個範例中到底是亮燈時長存在前面、還是切換次數存放在前面。要解決這個問題, 可以使用**結構 (`struct`)**。
`struct` 可以將多項資料集合成起來. 不過和陣列不一樣的是個別資料並不需要是相同型別, 也不是透過序號索引, 而是透過成員名稱來取用, 先來看看如何宣告結構:
```cpp
struct twinkle_data {
int level; // 亮燈時長 (us)
int total; // 切換次數
};
```
這樣我們就定義了名稱是 `twinkle_data` 的結構, 它含有 2 個成員, 分別是 `level` 與 `total`, 就像是在這個結構內可以放兩個變數一樣。
實際上要使用結構, 必須將結構的名稱當型別宣告變數, 像是這樣:
```cpp
twinkle_data s = {30, 5000};
```
或者也可以指明各資料的成員名稱, 像是這樣 (注意在成員名稱前有一個**英文句點**):
```cpp
twinkle_data s = {.level = 30, .total = 5000};
```
之後就可以透過成員的名稱取用資料:
```cpp
s.level // ----> 30
s.level = 100; // 設定成員的值
```
了解結構的基本用法後, 我們就可以將剛剛的程式改寫如下:
```cpp=
const int duration = 100; // 單次亮/熄時長 (us)
const int led_pin = 5; // 內建 LED 腳位
struct twinkle_data {
int level; // 亮燈時長 (us)
int total; // 切換次數
};
twinkle_data twinkle_datas[] = {
{30, 5000}, // 稍亮 0.5s
{5, 10000}, // 微亮 1s
{100, 2000} // 全亮 0.2s
};
void twinkle(int led_pin, int duration, int duration_on, int total) {
for(int count = 0;count < total; count++) {
digitalWrite(led_pin, LOW);
delayMicroseconds(duration_on);
digitalWrite(led_pin, HIGH);
delayMicroseconds(duration - duration_on);
}
}
void setup() {
pinMode(led_pin, OUTPUT);
}
void loop() {
for(int i = 0; i < sizeof(twinkle_datas)/sizeof(twinkle_data); i++) {
twinkle(
led_pin,
duration,
twinkle_datas[i].level,
twinkle_datas[i].total);
}
}
```
1. 4~7 行就是我們定義的結構, 這種結構的名稱叫做 `twinkle_data`, 稍後就可以用這個名稱當型別宣告變數。
2. 9~13 則是建立一個含有 3 個 `twinkle_data` 結構的陣列, 注意到因為陣列內容是結構, 設定個別資料時會有**多層的大括號**, 我們故意以**多行形式**書寫, 方便辨識陣列內個別資料的內容。
3. 第 29 行運用上一節介紹過的技巧, 計算 `twinkle_datas` 陣列內的資料數量。
4. 第 33 和 34 改用結構的成員提供亮燈時長與切換次數。
透過這樣的方式, 就可以隨意變更亮度變化層次, 而不會讓亮燈時長和切換次數的項目數量不一致了。
## 傳送結構給函式
我們可以更進一步, 把 `twinkle_data` 函式修改成以結構傳遞相關資訊, 就不需要傳送一大堆參數了, 修改過的版本如下:
```cpp=
const int led_pin = 5;
struct twinkle_data {
int pin; // LED 腳位
int duration; // 亮/熄總時長
int duration_on; // 亮燈時長 (us)
int total; // 切換次數
};
twinkle_data twinkle_datas[] = {
{led_pin, 100, 5, 10000}, // LED 稍亮 1s
{led_pin, 100, 30, 5000}, // LED 稍亮 0.5s
{led_pin, 100, 100, 2000} // LED 全亮 0.2s
};
void twinkle(twinkle_data data) {
for(int count = 0;count < data.total; count++) {
digitalWrite(data.pin, LOW);
delayMicroseconds(data.duration_on);
digitalWrite(data.pin, HIGH);
delayMicroseconds(data.duration - data.duration_on);
}
}
void setup() {
pinMode(led_pin, OUTPUT);
}
void loop() {
for(int i = 0; i < sizeof(twinkle_datas)/sizeof(twinkle_data); i++) {
twinkle(twinkle_datas[i]);
}
}
```
1. 3~8 行是修改後的結構, 把其他資訊也一併放入, 這樣不只內建接在 5 號腳位的 LED 可以適用, 其他腳位也可以用一樣的方式操控。
2. 第 10~14 行就依據新的結構建立陣列, 並設定個別結構內成員的值。
3. 第 16~23 行是修改過的 `twinkle` 函式, 現在只需要一個參數, 並在函式內以結構成員控制 LED。
4. 第 31 行現在就只需要傳送結構給 `twinkle`, 程式不但變得比較清爽, 語意上也比較清楚。