C
C言語の「成功」は何故0(ゼロ)なのか
Cに限らず、ある関数の実行後に真偽値(true / false)を返し、成功(正常終了)したか失敗(異常終了)したかを呼び出し元に伝えるという方法はよく使われると思う。
最近の言語ではほぼ例外なく、trueが成功、falseが失敗として扱われているはずだ。
しかしCの場合はその逆が慣例となっており、0が成功、0以外が失敗として扱われる。
下のような感じだ。
どうしてCだけが0を成功と見なすようになり、他の言語はfalseを失敗と見なすようになったのだろうか。
この理由にはプログラミング言語の歴史が関係している。
1972年に登場したCは、1999年発行のC99まで実に27年間もの間、真偽型(boolean型)が存在しなかった。
C99でようやく_Bool型が追加され、真偽値専用の型が使えるようになったわけだが、それまではint型で真偽値を代用するのが一般的だった。
実際Cはすべての組み込み型で真偽判定すると、0が偽になり0以外が真になる。
そんなCだが、関数が成功したことを意味する戻り値の「0」は「偽」という意味合いではなく、「異常はひとつも無い」「エラーは無かった」という意味の「0」として使われていたのだ。
Cが開発されたPDP-11というミニコンは部品数が多く、故障する可能性がある箇所が多かった。
成功は1つしか無いが、失敗(異常終了)には非常にたくさんのケースがあったわけだ。
「失敗」とだけ分かったとしてもそれだけでは原因解明に苦労するので、どんな失敗なのか、どこで失敗したのか、それをできるだけ具体的に返す必要がある。
今の時代の言語なら文字列で返すことが可能だが、Cには文字列型というものは無いし、仮に配列やヒープへのポインタを使ったとしてもそのメッセージを格納するだけでメモリを消費してしまう。
最大64KBというメモリ空間は可能な限りシステムやアプリケーションのために確保しておきたい。
そこで、どんな失敗かという情報を数値で返すようにした。
異常や失敗が何もなければ「エラー無し」を意味する0を返した。
つまりCの関数は成功したか失敗したかの真偽値を返していたわけではなく、エラーコードを返していたわけだ。
やがて時代が進むと、引数に必要な値は構造体に収めて、その構造体へのポインタを関数に渡し、関数からの戻り値やエラーの内容もその構造体へ直接入れて呼び出し元へ戻すようになる。
この頃になると関数はエラーコードではなく「エラーメッセージ」によってどんな失敗があったのかを呼び出し元へ伝えるようになり、戻り値は関数が正常終了したか異常終了したかということを、0か0以外かで返すようになった。
そういった時代的背景があるので、今でもCでは成功を「0」で返すことが多い。
しかし1990年代に入ってから出てきた言語の場合は、その逆になることがほとんどだ。
生まれながらに真偽型(boolean型)を持っている言語では、真偽値を「0」や「1」といった数値ではなく「true」「false」という予約語を使って記述する。
こうなってくると、「間違い」「誤り」を意味する「false」を成功扱いするのが不自然になる。
そこで、言葉上自然に見える「false」を失敗とし、trueを成功とする言語が増えてきた。
なので最近の言語からプログラミングの世界に入り、その後にCを勉強した人の中には、「成功 = 0 / 失敗 = 0以外」に違和感を覚えて_Boolの1で成功、0で失敗を返す人もいるようだ。
しかしこれをすると、既存のライブラリとの整合性が取れなくなるので、混乱を防ぐためにもCの成功は「0」が無難だと個人的には思う。
ただし例外もあって、例えばWindowsAPIはCで書かれているものの0を失敗扱いする関数が多い。
確かな理由はマイクロソフトにしか分からないが、Windowsでは0と1をそれぞれdefineしたマクロ「FALSE」と「TRUE」が多用されていて、上に書いたような理由で「FALSE」を失敗扱いしたほうが言葉的に自然だからではないかと思う。(といっても成功は0以外で返ってくる(成功が必ずしも1とは限らない)ため、FALSEかFALSEではないかで判定することになる)
さらに時代が進み動的型付け言語になると、失敗だけを真偽値の「false」で返し、成功時にはデータである数値型や文字列型、挙げ句は配列やタプルなどで複数の値を返すような関数・メソッドが増えてくる。(動的型付けなので、戻り値も型の決まりがない)
静的型付け言語も進化していて、2000年代に入ってから出てきた言語では、多値返しやタプル返し、成功時の値またはエラー内容のどちらかを格納できる型(例えばRustだとResult型)などで、成功失敗情報・値やオブジェクト・エラーメッセージを一緒に返せる関数を作れるようになった。
戻り値をreturnするのではなく例外をthrowしてtry~catchで掴むなど、値を返す以外の方法で呼び出し元に失敗を伝える手法も一般化したので、必ずしも関数が真偽値を返して成功・失敗を伝える必要はない時代だが、つい「return 0;」などと書いては「あ、Cじゃないんだっけ」となる私はもうきっと古い人間なんだと思う。
|