[[Cygwinでデバッグ]]
#contents

** 数学の表記との違い [#t1b0b7a8]

*** 等号と代入、そして lvalue [#s4ba0d4d]
数学で考えると、次の2つの等式は同じ意味です。
 (1) a = 2
 (2) 2 = a
さて、C言語のプログラムで考えると、少し事情が変わります。もしこれらが、if や while の条件式の中に現れて、左右の項を比較しているのだとすればどうでしょう。C言語での比較は == と2文字で表記することに注意します。
 (1') if (a == 2) { ... }
 (2') if (2 == a) { ... }
このように書けば、(1')(2') とも同じ意味になります。

しかし、もし代入を意味していれば、状況が異なります。以下の (1' ') は a に 2 を代入しますが、(2' ') は文法エラーになります。2 に a の値を代入することはできないからです。
しかし、もし代入を意味していれば、状況が異なります。代入の向きは右から左と決まっているので、以下の (1' ') は a に 2 を代入します。ところが (2' ') は文法エラーになります。2 に a の値を代入することはできないからです。
 (1'') a = 2;  // OK
 (2'') 2 = a;  // NG
これは、代入できるものは特別なものである、ということでもあります。= の左側の式は、英語では "lvalue" という、辞書には載っていない単語で表されます。日本語では「左辺値」との訳語があてられます。
これは、代入される(できる)ものは特別なものである、ということでもあります。= の左側の式は、英語では "lvalue" という、普通の辞書には載っていない単語で表されます。日本語では「左辺値」との訳語があてられます。

左辺値には変数が入っていればいいのかというと、そうでもありません。変数単独であればよいのですが、変数を用いた計算式には代入ができません。
左辺値は変数であればよいのかというと、そうでもありません。変数単独であればよいのですが、変数を用いた計算式には代入ができません。
 a   = 2; // OK
 a+1 = 3; // NG

同じように左辺値として x は大丈夫でも、x*x はエラーになります。もし x*x をある値にしたいのであれば、その逆関数である平方根を用いて x に代入するように式変形します。
 (a) x*x = 4;       // NG
 (b) x =  sqrt(4);  // OK
 (c) x = -sqrt(4);  // OK(?)
> (b)(c) のように、逆関数が1通りでない場合にどうすればよいのかは、プログラマが判断せねばなりません。


*** 不等号 [#j099ac58]
数学では「0≦x<10」のような表記をしますが、C言語の条件文としては、この表記では目的が達せられません。
 (1) if ( 0 <= x < 10 )      { ... }   // NG
 (2) if ( 0 <= x && x < 10 ) { ... }   // OK
 (3) if ( x >= 0 && x < 10 ) { ... }   // OK
(1) は、エラーにはなりませんが違う意味になってしまいます。正しくは(2)や(3)のように、2つの条件を && で結びます。一般に、2つの条件が同時に成りたって欲しければ &&、どちらか片方が成り立てばよいのであれば || をはさんで書き並べます。
> (1) の表現には、gcc だと警告レベルを上げると検出してくれます(→[[Cygwinでデバッグ/不可解な動作]])。Java ではエラーになります。
> ちなみに (1) の式は ((0<=x) < 10) と評価されます。「(0<=x)」の比較結果は(違和感があると思いますが)0 か 1 の数値で表現されます(→[[比較演算>#f925bdd3]])。それが 10 より小さいかが評価されるので、最終的には「常に成り立つ」ということになります。

(2) と (3) はどちらも同じ意味ですが、数直線を思い描く人はにとっては不等号の向きが揃った (2) の表現のほうが理解しやすいでしょう。変数が左にあるほうが落ち着く人には (3) が好まれるでしょう。

さて、上の条件の否定をどう書くのか考えてみます。いろいろなバリエーションが考えられます。
     if (  0 <= x  &&  x < 10  ) { ... } // 否定する前
 (a) if (!(0 <= x  &&  x < 10) ) { ... } // 単純に ! をつけて否定にした
 (b) if (!(0 <= x) ||!(x < 10) ) { ... } // ! を分配して && と || を入れ替え
 (c) if (  0 > x   ||  x >= 10 ) { ... } // ! をやめて不等号を入れ替え
 (d) if (  x < 0   ||  x >= 10 ) { ... } // 左に変数が来るように
 (e) if (  x < 0   ||  10 <= x ) { ... } // 不等号の向きを揃えた
どれも同じ意味ですが、よく使うのは (a), (d), (e) あたりでしょうか。

余談ですが、"<=" と "=<" のどちらか迷う人が多いですが、正しいのは片方だけです。覚え方は「+= も *= も <= も >= も、''すべて = が後ろ''」です。
> C言語では = がいつでも後ろですが、Perl では残念なこと(?)に "=~" という演算があります。
> C言語の2文字の演算子は = がいつでも後ろですが、Perl では残念なこと(?)に "=~" という演算子があります。


*** 比較演算 [#f925bdd3]
数学で考えれば a+b と a<b は計算の質が異なります。a+b はいつでも計算できます。反面 a<b は計算するものではなく、条件として扱うものです。

ところが、C言語をはじめとする多くのコンピュータ言語では、a+b と a<b のどちらも計算式であって、計算結果としての値を持ちます。C言語でも数学でも、a+b は当然のようにaとbの和です。そしてC言語では、a<b は成り立てば int 型の 1、成り立たなければ int 型の 0 の値になります。変数の a, b の型によらず、比較結果は必ず int 型になります。
> 1 とか 0 とかの値は、言語によって若干のバリエーションがあります。太古の BASIC インタプリタでは整数型の -1 と 0 でした。Java では boolean 型の true と false という値になります。

つまり、c = a+b とすれば c に a+b の値を代入できるように、c = a<b のような代入も可能です。従って、次のような書き換えもできてしまいます(が、あまり使いません)。
 if ( a < b ) { ... }    // 短縮(?)版

 int cond = (a < b);     // 分割版
 if ( cond )  { ... }

次はよく使う例です。無限ループを書くのに、
 while ( 0 == 0 ) { ... }
とする流儀がありますが、0==0 の演算結果は、成り立つので 1 となります。ですから、
 while ( 1 )      { ... }
と表記するのと同じ意味になります。
> 条件式は 0 以外は成立とみなされるので、while (2) {...} でも無限ループです。

0 かどうかを判定する関数 is_zero() は次のような2通りの書き方が可能です。 
 int is_zero(int x) {
     if (x == 0) return 1;
     else        return 0;
 }

 int is_zero(int x) {   // 短縮版
     return (x == 0);
 }
上のような return の書き方は多用されているわけではありませんが、呼び出し側には注意が必要です。常套句があって、次のように書きます。これは頻繁に用います。
 if ( is_zero(a) ) { ... }
if の条件式の中で比較を行っていないことに注目して下さい。比較は関数(短縮版)の中ですませているので、if の条件式では 1 と比較することすらしないのが流儀です。この流儀を用いるのはどういう場面かというと、関数名・変数名が is で始まっている時です。プログラムが英語の文章のように読めるでしょう、if a is zero, then ... のように。

否定の条件は次のように書きます。
 if ( !is_zero(a) ) { ... }
これは if a is not zero, then ... のように理解します。
> C言語の標準関数に isalpha() のようなものがあります。不成立なら 0 を返しますが、成立した場合に 1 ではなく、64 のような値を返すことがよくあります。ですから 1 と比較すると間違ったプログラムになってしまいます。

> C 言語ではこのような関数・変数に int 型を用いるため、バラツキに対する注意が必要です。C++ や最新の C99 には bool 型があるのですが、やはり内部的な値にバラツキがあるので、同じ注意が必要です。安心なのは Java の boolean 型で、true と false の2通りの値に制限されています。


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS