Release 2018.4.23 / Update 2018.6.28

条件式

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”であるか?」という事でしか判別していない。

参考リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください