前回の記事で作成したプログラムを、よりわかりやすく改良します。
第10回から第12回までの内容は、少し難しくなっています。
すべて理解できなくても、第13回以降の記事がわからなくなる、ということはありません。理解できるところまでチャレンジしてみましょう!
もし難しく感じたら、とりあえず第12回の記事まではスキップして、第13回の記事からお読みいただければと思います。
前回のスケッチの問題点?
前回までの記事で、「スイッチを押したら、LEDがピカっと1回だけ光るスケッチ」を作成しました。
きちんと動作しますので、文句のつけようがないスケッチです。
あとは、「スイッチを押したら、文字データを1回だけPCに送るスケッチ」を作成すれば、オリジナルのキーボードができそうです。
でも…
ちょっと不思議に思われるかもしれませんが、このきちんと動作するスケッチに、まだちょっと問題点が残っているんです。
その問題点とは、今後ご自身で規模が大きいスケッチを作るようになると、目立ってきます。
今回の記事で習得する内容は、他の人が書いたスケッチでも見かけることが多いので、ぜひ一緒にその問題点と解決策を習得していきましょう!
スケッチの問題点とは?
前回のスケッチをもう一度確認してみましょう!

変数zenkai
に注目してみると、変数宣言の位置と変数を使用する位置が離れています。
結果として、次のような分かりづらさがあります。
zenkai
をグローバル変数にしているので、変数の宣言はプログラムの最初の方にあり、パッとみただけだとこの変数がスケッチのどこで使われているのかわかりづらいloop
関数だけ見ると、関数内で変数zenkai
が使われているものの、loop
関数内にこの変数の宣言がない(変数型がすぐにわからない、などの問題がある)- 変数
zenkai
はloop
関数だけで使用するものなのに、グローバル変数にしているので他の関係ない関数(setup
関数)からも利用できてしまう
これらの問題点は、スケッチの規模が大きくなってくると目立つようになってきます。
これらの問題点の本質は、「変数の宣言」と「変数が使われるところ」が離れているという点にあります。
この問題点を解決するには、実際に変数が使われる関数内で宣言するしかありません。
ところが、変数zenkai
をloop
関数内で宣言すると、loop
関数の最後の}
で変数は捨てられてしまいます。
ということは、「loop
関数内で、変数zenkai
を宣言しても捨てられない変数宣言」ができれば問題解決って感じですよね。
実は、C++言語ではこのような目的のための便利な仕組みが用意されているんです。
関数内で変数を宣言した場合でも、その関数の処理が終わっても(つまりその関数の最後の}
にきても)、その変数を捨てられないようにしてもらえる仕組みです。(なんだかややこし表現ですみません…)
その仕組みを具体的に見ていきましょう!
static変数
変数を関数内で宣言すると、その変数の寿命はその関数が終わるまででした。(ローカル変数の特徴ですね)
ところが関数内で変数を宣言するときに、変数宣言の先頭にstatic
を付けると、その変数は関数の処理が終わっても捨てずにそのままにされます。

この「static」は変数宣言を修飾していることから、「修飾子」と呼ばれることもあります。(語学の文法でも「修飾詞」という用語が出てきますよね。「子」と「詞」の違いがありますが…)
「static」は日本語で「静的」という意味ですが、ちょっとわかりづらいですよね。
staticをつけて宣言した変数は、関数の処理が終わってもずっとそのままじっと静かに存在している、ということで「静的」というようなイメージですね。
そのため、static変数は「静的変数」と呼ばれることもあります。(グローバル変数も「静的変数」です!)
一方、ローカル変数は関数内で用意され、関数が終わると捨てられるので「動的変数」と呼ぶこともあります)
static変数を使ってみる
それでは、実際に前回のスケッチを変更してみましょう。
変数zenkai
の宣言をグローバル変数ではなく、loop
関数内に移します。
この変数は関数の処理が終わってもそのままにして欲しいので、次のように先頭にstatic
をつければOKです。
static uint8_t zenkai;
それでは、static変数に変更したloop
関数を確認してみましょう。2行目で変数zenkai
をstatic
として宣言しています。
void loop() {
static uint8_t zenkai; // 前回のスイッチ状態を記録する変数
uint8_t konkai; // 今回のスイッチ状態を記録する変数
// 今回のスイッチ状態を読み取る
konkai = digitalRead(SWITCH_HIDARI);
// スイッチ状態が、前回OFF、今回ONの時、スイッチが押されたと判断する
if( (zenkai == OFF) && (konkai == ON) ) {
digitalWrite(LED_BLUE, HIGH); // LEDを点灯
delay(TENTOU_JIKAN); // 一定時間待つ
digitalWrite(LED_BLUE, LOW); // LEDを消す
}
// 今回の値を前回の値として保存
zenkai = konkai;
}
これでプログラムの変更は終わりました。
ところで、細かい話になりますが、ちょっと気になるところがありませんか?
Arduino Microの電源が入っている間、loop
関数は何度も何度も繰り返し実行されます。
ここで、変数konkai
とzenkai
を比較してみます。
変数konkai
は、普通の変数宣言ですので、loop
関数の処理が始まるとuint8_t konkai;
で変数が用意され、loop
関数の最後の}
でその変数は捨てられます。Arduino Microは、loop
関数を処理するたびに、変数を用意して、それを捨てる、ということをしています。
一方で、変数zenkai
は、一度用意されたらloop
関数の最後の}
のところで捨てられることはなく、ずっと存在し続けます。でもloop
関数は何度も何度も繰り返されるので、何度も何度もstatic uint8_t zenkai;
という変数が宣言されてしまいますよね。何度も変数宣言しても大丈夫なのか?とちょっと不安になりませんか?
でも安心してください!Arduino Microはstatic
の意味をちゃんとわかっていますので、static uint8_t zenkai;
により変数zenkaiを一度用意したら、再度static uint8_t zenkai;
に出会っても用意した変数zenkai
をずっと使い続けます。
static変数をうまく使うとスケッチが読みやすくなるので、ぜひ活用してみてください!
static変数の初期値
ところで、勘の鋭い方でしたら気付かれたかもしれません。
普通の変数の宣言をした場合は、適当な数字が書かれた変数が用意される、と説明しました。
今回変更したスケッチでは、static uint8_t zenkai;
とただ宣言したので、適当な数字が用意されてしまうのではないか、という心配があります。
実は、static変数宣言の場合、0が代入された変数が用意されます。(グローバル変数と同じ扱いですね)
上のスケッチ例では、0が代入されたstatic変数zenkai
が用意されることになります。
ところで、今回使用する変数zenkai
はスイッチはOFFから始まりますので、初期値としては「1」が代入された変数を用意して欲しいところです。
このようにstatic変数を宣言するときに、初期値を指定したい場合は次のように=
で初期値を代入すればOKです。
static uint8_t zenkai = 1;
loop関数の中にこのように書いても、この変数宣言が実行されるのは1回だけです。
loop
関数の処理のたびに毎回1が代入されることはありませんので安心してください。
staticのその他の働き
今回の記事では、変数宣言の修飾子として使用する「static」を説明しました。
これから他の人が作ったプログラムを見る機会もあると思いますが、この「static」は関数定義にも使用されることがあります。
関数の前に「static」をつけるわけですが、その時の「static」は今回説明したstatic変数とは別の意味を持ちますので注意してください。(関数定義に「static」をつけた場合の働きについては基礎編の範囲を超えますので説明は省略します…すみません…)
static変数を使用したスイッチ検知スケッチ
前回のスケッチを、static変数を使用して次のように変更してみました。
動作は変わりませんが、できれば動作確認してみてください。
/*
* スイッチ制御プログラム static変数版
* スイッチを押すとピカッとLEDを光らせる
*/
// 青色LEDのピン接続番号
#define LED_BLUE 12
// 左側スイッチのピン接続番号
#define SWITCH_HIDARI A5
// スイッチの状態
#define OFF 1
#define ON 0
// 点灯時間(単位:ms)
#define TENTOU_JIKAN 50
void setup() {
pinMode(LED_BLUE, OUTPUT); // LEDのピンを出力に設定
pinMode(SWITCH_HIDARI, INPUT_PULLUP); // スイッチのピンを入力(プルアップ)に設定
digitalWrite(LED_BLUE, LOW); // 最初はLEDをOFFに設定しておく
}
void loop() {
static uint8_t zenkai = OFF; // 前回のスイッチ状態を記録する変数
uint8_t konkai; // 今回のスイッチ状態を記録する変数
// 今回のスイッチ状態を読み取る
konkai = digitalRead(SWITCH_HIDARI);
// スイッチ状態が、前回OFF、今回ONの時、スイッチが押されたと判断する
if( (zenkai == OFF) && (konkai == ON) ) {
digitalWrite(LED_BLUE, HIGH); // LEDを点灯
delay(TENTOU_JIKAN); // 一定時間待つ
digitalWrite(LED_BLUE, LOW); // LEDを消す
}
// 今回の値を前回の値として保存
zenkai = konkai;
}
更新履歴
日付 | 内容 |
---|---|
2021.9.12 | 新規投稿 |
2025.2.10 | 説明内容簡略化 Arduino IDE2対応 |