第27回 変数の詳細(1) 〜変数のスコープ〜

今回は変数について理解を深めます。

目次

今回の説明内容

前回までの記事では、変数の基本的な使い方について習得してきました。

変数はメモ用紙のようなもので、そのメモ用紙を使うには最初に「サイズ」と「タイトル」を指定してメモ用紙を用意してもらいました。

メモ用紙を一度用意してもらうと、そのメモ用紙に数字を書き込んだり、計算したりすることができます。

実際に日常で使うメモ用紙であればここで話が終わりなのですが、C++言語で使うメモ用紙(変数)は日常で使うメモ用紙ほど自由ではないんです。

そこで、今回の記事では変数の性質についてさらに理解を深めます。ちょっと難しいところもあるかもしれませんが、じっくり読み進めれば大丈夫です!

変数の基本の復習

繰り返しになってしまいますが、前回までの変数の基本を振り返っておきたいと思います。

大丈夫だよ、という方はこのセクションは飛ばしていただいて問題ありません。

変数の宣言

変数を使う場合、必ず「型」と「名前」を指定してあらかじめ用意してもらう必要があります。このことを「変数の宣言」と呼んでいます。(なんだか仰々しいですが…)

uint8_t count;  // uint8_t型(0〜255)の「count」という名前の変数を用意する

また、同じ型の変数をいくつか用意する場合、次のようにカンマで区切って並べて宣言することもできます。

uint16_t count, timer;  // uint16_t型(0〜65535)の「count」と「timer」という名前の変数を用紙する

変数の初期化

Arduinoに変数を用意してもらうと、「適当な数字が書かれた」変数が用意されます。

例えば、用意してもらったcountという変数で何かの回数を数えるときは、count++;などで数えていきます。

ところが、用意してもらった変数には適当な数字が書かれていますので、数を数えるためには0を書いてからcount++;していく必要があります。

このように変数を使う前に何かの値を代入しておくことを「変数の初期化」と呼んでいます。

変数の初期化は変数宣言するときに次のように書くことができます。

uint8_t count = 0;  // uint8_t型の「count」という変数名を0で初期化して用意する

また、いくつかの変数をまとめて初期化して宣言することもできます。

// uint16_t型の「count」と「timer」という名前の変数を0で初期化して用意する
uint16_t count = 0, timer = 0; 

変数の利用

変数に値を代入するには「 = 」記号を使います。

count = 0;  // countに0を代入する

また四則演算などの計算もできます。

uint16_t apple_price, orange_price, total_price;  // 変数宣言

apple_price  = 198;  // りんご1個の値段
orange_price = 50;   // みかん1個の値段

total_price = apple_price * 4 + orange_price * 8  // りんご4個とみかん8個の合計金額

変数の基本的な使い方はこれで十分です!

ちょっとした疑問

キッチンタイマーのスケッチでは、変数を利用してLEDの点滅回数、つまり時間を計測することができるようになりました。

変数の扱い方は少し理解できてきたと思いますが、もしかしたらちょっとした疑問がある方もいるかもしれません。

その疑問とは、変数の宣言の位置です。

特に詳しい説明もなく、時間を計測するための変数として「count」という名前の変数をforの「初期化」部分で宣言しました。

変数の宣言というのは、Arduinoに「〇〇という型の□□という名前の変数を用意してください」と依頼するものでしたよね。

変数を用意してもらうように依頼するだけですので、もっと早い段階で宣言してはいけないのでしょうか?

例えば、次のようにsetuploopの部分よりもっと前に変数を宣言するとどうなるのでしょうか?

上のように、ずっと上の位置で変数を宣言しておけば、その段階で変数を用意してくれるので問題なさそうですよね。でも本当にいいのかな?という気もします。

今回の記事では、このちょっとした疑問から出発して、変数の性質について理解を深めていきます。

変数を宣言する場所のルール

変数を宣言する場所のルールは、大きく2つあります。以下、それぞれのルールについて説明します。

1.変数を使う前に宣言する

一つめのルールは「変数を使う前に宣言する」というルールです。

次のように変数を宣言する前に変数を使うと、例のごとく「そんな変数は知らない!」と英語で怒られます。

「いやいや、最後の方で宣言してるのに…」って気もしますが、Arduino IDEはスケッチを上から1行ずつ(1文字ずつ)読んでいきます。先読みはしてくれませんので、使うあとで宣言してもダメなんです。


ところで、変数の宣言は変数を使用する前であれば大丈夫なのはいいとして、前と言ってもそれなりに範囲がありますよね。

そこで、変数の宣言場所のルールの二つめを確認しましょう。

2.宣言した変数は、その変数が属する「 } 」まで有効

タイトルは「なんのこっちゃ?」って感じですが、とても重要なルールです。じっくり理解していきましょう。

言葉だけの説明ではわかりづらいので、実際のスケッチを見ながら確認していきます。


キッチンタイマーで時間を計測するときに使用した変数countは、forの初期化の部分で宣言しました。

このとき、変数countは宣言した直後から使えますが、どこまで使えるかというと、

countが属する}まで

になります。

変数countは、forの処理の部分(つまり{から}までの部分)に属しますので、for}までということになります。(ややこしいですが、次のイラストをじっくり見てみてください)


今の説明で「変数が属する」と説明しましたが、「属する」の意味を他の例でも確認しておきたいと思います。

次のスケッチでは、変数countをloopの直後で宣言しています。

変数countは、loopの{}で囲まれた内部で宣言しています。つまり、変数countはこの{}に属しています。

ということで、変数countは、変数を宣言した直後から、その変数が属する}まで有効ですので、countが使える範囲は上のイラストのようになります。


ところで、このスケッチをよくみると、forの処理部分にも{}がありますよね。

このように{}の内側にさらに{}がある場合、その内部の{}で囲まれた部分でも使うことができます。(つまり一つ前のイラストに示している範囲がcountが使える範囲になります)

このように変数は、宣言した階層より下の階層の{}の内部でも使用することができます。(階層が何重になっていても、変数宣言したところより内部であれば使用できます)


それでは、次の場所で変数countを宣言した場合はどうなるのでしょうか。

この場合、countsetuploop{}の1階層上で宣言していますよね。つまりスケッチのトップ階層というイメージですかね。

countが使える範囲は、宣言した直後から、変数宣言した階層より下の階層にあるすべての{}の内部でも使用することができます。つまり、次のように宣言した直後からスケッチの最後まで使用することができます。

最初はちょっとわかりづらいかもしれませんが、変数をどこで宣言しているかを明確にすれば、どこからどこまが使えるか、ということがわかります。

変数のスコープ

このように、変数は宣言した位置で使用できる範囲が決まります。

この使用できる範囲のことを「スコープ」と呼んでいます

「スコープ」は日本語では「範囲」という意味なので、「変数の有効範囲」とか言えばいいような気もしますが「変数のスコープ」と呼ばれることの方が多いように感じます。(スコープの方が難しい話をしているような感じがしますし)

ローカル変数とグローバル変数

変数には使える範囲(スコープ)のルールがあることがわかりました。

ここでさらに変数を使用できる範囲の観点から変数の種類を分類する言葉がありますので、確認しておきたいと思います。


先ほどの例のように、スケッチの最初の方で、どの{}の内部にも属していないところで変数を宣言すると、その宣言した行以降からスケッチの最後まで使うことができます。

このように宣言した変数はスケッチの広範囲で使用できるため「グローバル変数」と呼んでいます。「グローバル」は日本語で「全体的な」というような意味です。


一方、タイマーの時間計測で使用した変数countのように、特定の{}で宣言した変数は、その内部のみで使用できます。

このように部分的に使用できる変数を「ローカル変数」と呼んでいます。「ローカル」は日本語で「局所的な」というような意味です。


ところで、変数にはスコープがあり、宣言する場所によってグローバル変数とローカル変数があることはわかったのですが、またちょっと疑問が出てきてしまいました。

例えば、タイマー時間を計測するために「count」という名前の変数を宣言する場合、グローバル変数で宣言しても、ローカル変数で宣言しても、どちらも問題なく動作します。

では、変数の宣言はどこで行うべきなのでしょうか???

グローバル変数で宣言すればどこでも使えるので、全部グローバル変数で宣言してしまった方が深いことは考えずに済むので、グローバル変数一択って感じがしませんか?

そこで、変数はどのように宣言するのが適切なのか、次回の記事で解明していきます。

Arduino IDEのエラーの意味

今回の記事はここまでですが、もう少し深掘りしたいと思います。今後の記事の理解には影響しませんので、読み飛ばしていただいても構いません。

この記事の最初の方で、変数宣言の前に変数を使用するスケッチを検証してみました。当然ながらエラーになりました。

上の例ではエラーの内容がウィンドウの下の方に表示されています。Arduino IDEが何を言っているのか解読してみましょう。

何やら英語でグダグダ文句を言われていますが、ポイントとなるところは次の部分です。

kitchen_timer.ino:41:7: error: 'count' was not declared in this scope
   for(count=0;  count<TIMER_JIKAN; count++) {
       ^~~~~

kitchen_timer.ino:41:7: note: suggested alternative: 'round'
   for(count=0;  count<TIMER_JIKAN; count++) {
       ^~~~~
       round

exit status 1

Compilation error: 'count' was not declared in this scope

日本語では次のような内容です。(多少意訳部分があります)

kitchen_timer.inoの41行目の7文字目のところ: 困ります。次の'count'って変数宣言されてません!
   for(count=0;  count<TIMER_JIKAN; count++) {
       ^~~~~ここ、困ります

kitchen_timer.inoの41行目の7文字目のところ: Arduino言語に'round'があるんですがもしかして'round’?
   for(count=0;  count<TIMER_JIKAN; count++) {
       ^~~~~ここ、もしかしてArduino言語の'round'って書こうとしたの?

でも意味わかんないんで、これ以上付き合えません!

結論: 'count'って変数がこのスコープで宣言されてないんですけど!

ということで「スコープ内で正しく変数が宣言されていない」と怒ってますね。(安定のしつこさですね…苦笑)

でもスケッチをよく見ると、変数countはスコープ内の最後で宣言していますよね。

「スコープ内で宣言されていない」という指摘は厳密には正しくはありません。でも相手が怒っている時は、揚げ足取りは逆効果です。わかりました、ということで、Arduino IDEに怒られたら素直に従ってスケッチを修正するようにしましょう。

更新履歴

日付内容
2019.9.11新規投稿
2021.8.25新サイトデザイン対応
2022.2.15for文の説明追加
2024.12.8Arduino IDE2対応
通知の設定
通知タイミング
guest
2 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
オガワ
オガワ
1 年 前

この、「scope」というのは思考の範囲を指すタイプの「範囲、領域」って意味みたいですね。
「領域展開(某漫画に出てくるエリア限定施行者無敵固有空間)」が「Area expansion」となる事から察するに、プログラムコードを文字の羅列と捉える前に、あるひとつの脳みそ?的に捉えている感じがします。
そう思うと、エラーで「(上の記憶から順番に考えてるのに)考えられる範囲に定義がなくて困るんですけど!」となるのはまあ、確かにって思いました。

最終更新日 1 年 前 ( オガワ )
目次