Release 2018.4.23 / Update 2018.6.28

(日本語) 条件式

Sorry, this entry is only available in 日本語. For the sake of viewer convenience, the content is shown below in the alternative language. You may click the link to switch the active language.

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

参考リンク

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA


This site uses Akismet to reduce spam. Learn how your comment data is processed.