目次 [ Contents ]
Arduinoで扱う(C言語ベースの)プログラミングは、独学で何となくやってきたので、結構基本的な部分の理解が欠けていたりします。というか、まだ全然知らないことばかりで(苦)。
if文などで使う条件式に関しても、後年までちゃんと理解してなかった事がありました。日頃の記事で散々使っているから「今更」なんですけど、これからArduinoを始める方のために、この根本的な部分を取り上げてみようと思います。
条件式の基本
まずは基本的な話です。
条件式では主に変数や定数の比較を行い、その条件に合致しているかどうかで分岐を作ります。
void setup() { Serial.begin(38400); } void loop() { static byte value = 0; Serial.print(value); if (value == 0) { // 条件 Serial.println("*"); // ~合致している場合 } else { Serial.println("!"); // ~合致していない場合 } value++; delay(100); }
static変数「value」を宣言し、ループ毎に1ずつ足していきます。その中で「value」が条件に合致した時/しなかった時の命令分岐を作る、という簡単な例です。シリアルモニタ上で、合致すれば「*」、しなければ「!」が付加され表示されます。
ループは無限に繰り返されるので、1バイト型であるvalueは256でオーバーフローし、0に戻ります。
条件を比較するための「比較演算子」は以下の通り。
a > b | aがbより大きい場合 |
a >= b | aがbより大きいか、同じ場合 |
a < b | aがbより小さい場合 |
a <= b | aがbより小さいか、同じ場合 |
a == b | aとbが同じ場合 |
a != b | aとbが異なる場合 |
変数代入は「=」だけど条件式のイコールは「==」、と慣れないと書き間違える事は良くあると思いますが(今でもたまにある…)、まあ、全然難しい話ではありません。
条件式の振る舞い
で、ここからが書きたかった部分です。
if文の()内は、中の式が合致する(true)か、しないか(false)を見ています。上記の場合、value が“0”かどうかという事になります。
で、変数は実際の数値として処理され、比較演算子に沿うかを見ます。そして、条件式は合致するとtrue(1)、しなければfalse(0)という「数値」にさし代わります。その真偽を受けて分岐へ導かれて行く、という手順になっているわけです。
回数 | 条件式 | 実数を適合 | 真偽結果 | 判断 |
1ループ目 | if(value==0) | if(0==0) | if(true “1”) | 合致 |
2ループ目 | if(value==0) | if(1==0) | if(false “0”) | 不合致 |
3ループ目 | if(value==0) | if(2==0) | if(false “0”) | 不合致 |
… | … | … | … | 不合致 |
256ループ目 | … | if(255==0) | if(false “0”) | 不合致 |
257ループ目 | … | if(0==0) | if(true “1”) | 合致 |
つまり、条件式というのは、単に「true or false(0か1)」を導き出すものに過ぎないという事になります。
応用的な書き方
そうなると、上記サンプルコードのif文は、こう書いても同じ結果だという事が分かります。
void loop() { static byte value = 0; Serial.print(value); if (!value) { // 条件 Serial.println("*"); // ~合致している場合 } else { Serial.println("!"); // ~合致していない場合 } value++; delay(100); }
「!」は真偽を反転するものなので、“0”はtrue(1)に、“それ以外の数値”はfalse(0)に変換されます。valueは0になる時があるので、反転すると1(true)になる、というカラクリです。
また、これを知れば、単純に「計算式」でも成立できるということも理解出来るようになります。例えば、1回おきに合致するようにしたければ、
if (!(value % 2)) {
「%」は割った余りを出すものなので、2で割ると、余り(結果)が0,1,0,1,…と続くことになるからです。
もし条件式として書くなら、「if((value % 2) == 0) {」です。が、計算結果が“0”と“1”、或いは“0”と“それ以外”へ分かれるような計算式にすれば、条件式でなくとも成立するというわけです。
while文等で使う他の条件式でも、これらは一緒です。whileを以下のような使い方をしている場合が良くありますが、これも理屈が分かれば、ナルホドと思います。
void loop() { for (byte value = 0 ; value < 255 ; value++) { Serial.print(value); if (!value) { // 条件 Serial.println("*"); // ~合致している場合 } else { Serial.println("!"); // ~合致していない場合 } } while (1); }
for文で指定回数繰り返した後、while文に入っていくんですが、条件の中には1という数値が入っています。変数ではなく、永遠に変わることの無いtrue(1)なので、プログラムはこのwhileでずっとループし続けることになります。つまり、このwhile(1)はストッパーとして機能していることになります。
符号付きの場合
条件式というか、「真偽」についてなんですが、例えばvalueがマイナスを持つ符号付きの型の場合どうなるか?っていう話です。
void loop() { static char value = 0; Serial.print(value, DEC); if (!value) { // 条件 Serial.println("*"); // ~合致している場合 } else { Serial.println("!"); // ~合致していない場合 } value++; delay(100); }
char型は-128~127からなる符号付きの1バイトです。
これも結局、「0」以外の数値は、マイナスを含め「1 (true)」として判断されます。これは「符号付き」でのマイナスというのが、型の最上位ビットで判別しているためです。
符号付き (char) | ビット表記 | 符号なし (byte) |
0 | B0000 0000 | 0 |
1 | B0000 0001 | 1 |
2 | B0000 0010 | 2 |
… | … | … |
127 | B0111 1111 | 127 |
-128 | B1000 0000 | 128 |
-127 | B1000 0001 | 129 |
… | … | … |
-1 | B1111 1111 | 255 |
Char型とByte型のバイナリ配置について、間違った認識をしていたので、表を修正しました。 2018/6/28
つまり、「真偽」でのtrue or false (1 or 0)というのは、厳密に言うと、「“0”か“1”か?」ではなく、「“0”かどうか?」という事になります。言葉で書くとそんな差異はなさそうな感じもしますが、プログラミング上ではかなり重要な違いで、注意しておく必要があります。
「!」で、“0”がtrueになり、“それ以外”はfalseとなる理由もこれで良く分かる、かと。
まとめ
条件式というのは、“1”か“0”という真偽を導き出すためのものであり、真偽(true or false / 1 or 0)は、正確には「false(0)であるか、それ以外か」という事である。
つまり、if文を始めとする()内の「条件」というのは「“0”であるか?」という事でしか判別していない。
参考リンク