見出し画像

引き数はどこまで信用できるのか - C言語の呪い

C言語の特徴として言語としては何かを保証してくれることがあまり無いので、関数を書くときの引き数に無効なメモリへのポインタであったり、想定外のものが渡されてくることがあります。このため値をそのまま使うと自分が行う処理で思いもしない動作をしてしまうことがあります。

ここで、どのようにすれば良いのかというと、必ずしも「正解」はありません。

適切ではない引き数を渡してくるのは、渡すほうが悪いので、いちいち面倒は見ないというのもアリです。引き数をチェックするのにもコストがかかりますし、チェックの過程にバグを仕込んでしまうことすらあります。ただ自分の関数がどのような前提で動作するのかを何らかの形で「表明」して置かないと「そんなこと書いてないやん」というツッコミをうけるだけになります。

他の人に何らかの説明をするのであれば、普通はヘッダファイルのプロトタイプ宣言のあたりに書いておきます。本体側に書いてもソースを共有していることもありますが、必ずしも必要ではないので、見てもらうチャンスが無いかもしれません。ヘッダであればincludeする都合上、確認してもらえるチャンスがあります。もちろんきちんとしたドキュメントを用意するのが本筋ですし、ライブラリの形で配布するのであれば man ページを作っておくのが親切です。

もちろん本体側に適切な說明なりコメントを書くことはしたほうが良いですが、これはどちらかというと自分であったり本体自身をメンテナンスする人に対する情報です。

想定外の動作が発生した時には、当然ですが、その原因を探すことになります。いきなり無効なポインタを踏んでメモリ保護違反を喰らうとか、最終的に呼び出したライブラリ関数内で NULL を踏んで落ちるなどが良くあります。またループを抜けられなくなって終わらなくなったり、処理が終わってから思い出したように解放済みのメモリを使われておかしくなることもありますよね。その時には大抵、落ちた場所からのスタックトレースを見ることになるので、そこに現れる関数に対しては「取り敢えず」疑いの目が向けられます。調べてみれば冤罪であることも多いのですが、できれば自分の関数の中で落ちるのは避けたいという気持ちが働きます。そこで最低限のチェックだけはしておきたいということになります。

C言語には、こういう時に役に立つ assert というマクロが存在します。

assert - 診断機能

これは、引き数として「この条件は成立するはずだ」という式を書くことで、この前提が崩れた際にはメッセージを表示してプログラムを終了させるものです。NDEBUGを指定することで、このマクロ自身が無くなってしまうので、テストが終了しリリースビルドをする時には影響を与えません。よってパフォーマンスのことをあまり考えずにチェックをすることができます。もちろん実際に航海する時になってから救命胴衣を捨ててしまうようなことをするのはどうなのよ。ということもあるとは思いますが、そのような場合は自分で必要に応じた適切なコードなりマクロなりを用意すれば良いだけです(rassertlog という名前でロギングだけするような自作マクロを使うこともありました)。

このマクロがコードにあることにより、少なくともコードを読む人に対して、何を仮定しているのかを明示することが出来ます。単なる NULL チェックなどにも使えますが、どちらかというと config が正しく機能しているかを確認するようなややこしい式を書くこともあります。

既存のコードを見るとUNIXな世界のポリシーとして、将来の拡張の余地を残しておくというのを感じることが多いです。仕様にない条件について、そのすべてを否定するような厳しいチェックを行うべきではなく、どうしても必要な条件だけをチェックして、機能が拡張された時に邪魔をしないようにすることが求められているようです。解釈できない値が出てきた場合、それを使わないのであれば、そのままスルーして処理を継続してしまった方が得策だということです。こうしておけば、追加の機能が作られた時に後続の関数で、それを処理するようにして、既存の処理を温存できます。このやり方に対して思うところがある人も多いのではと思いますが、このように書かれているコードに出会うことも多いのは確かです。

多くのグループがコードを分担して作成する場合、その「責任分界点」を超えてバグが忍び込むことにシビアになるのは確かで、境界となる関数で非常に厳しいチェックを行いたくなることも多いのですが、自分に責任がないことを確認するよりも、適切とは言えない値を受け取った場合であっても、いかにスマートに処理を続けるかのほうが、結果的にはうまく機能するとは思っています。これは対象となる処理や目的によって、具体的な解決はかなり異なるとは思いますが、人間関係だけでなくコード上でも協調性を発揮するのが安心して使えるプログラムになると思います。受け取るものに対してはやさしく、自分が渡すものに対しては逆に厳しい目で安心できる値だけを渡すように心がけるのが大切ではないでしょうか。

ヘッダ画像は、以下のものを使わせて頂きました。

https://www.irasutoya.com/2017/03/blog-post_17.html


この記事が気に入ったらサポートをしてみませんか?