「C言語教室 第7回」ー後書きー
先日の「C言語教室 第7回 - 動的なメモリ割り当て」の回答を取り上げていただきました。
ありがとうございます。
そうすると、またいろいろ考えてしまふ(笑)。
もう少し書き足してみます。
動的メモリ
今回は動的メモリに関する課題でした。
私は、
・動的にメモリを確保する関数と、
・解放する関数を
2つ用意しましたが、実際の設計では少なくとも更にもう一つ被せます。特に複数人で開発する場合、確保解放の実装を各人に任せるということはしません。私の場合は特に連続運転のフォールトトレラントシステムを開発してきたので「解放忘れ」という問題を残すと致命的な現象につながります。
「各人に任せない」というのは必ずしも「各人の設計能力を疑っている」というわけではなく、こういうレベルの設計は局所的に閉じ込めてしまうのが効率的であるということによります。各人に負わせるのは無駄でしかありません。「解放忘れてない?」という些末なことにとらわれるのは煩わしい。各人には各人に、それぞれもっと重要で検討しなければならない設計が多々あります。それに集中すべきであって些細なことに注意を払わなければならないというのは主眼となる設計箇所も十分な検討が払われないという状況を招きかねません。同じような問題をシステムに関わる全員に負わせるのはできるだけ避けるほうがいい。
それから。
「free」の後でもライトしたデータはしばらく残っていることはあります。但し、残っているからといって好きにアクセスしていいというわけではありません。次に誰かが「malloc」したらそちらに割り当てられてしまうことはあります。「free」した以上、もう私のものではありません。
私も試してみましたが、「free」の後にはまだデータは残っていました。でも次に「malloc」したときゼロクリアされていた。「malloc するまで使用することはないから放置」ということのようです。
三項演算子
kzn さんの回答では「三項演算子」を使用されています。
実は、私は「三項演算子」を使ったことがない(笑)。
「三項演算子が苦手」という人が一部にあって(私も(笑))、請負でソフトウェアを作っていることから、できるだけ使わないというのが理由ではあります。
ただ、この「三項演算子」、この演算子にも「副作用を回避できる」というメリットがあります。最近、私は「副作用」についても、ああだこうだを言っているので、これについてもボソボソと話したいですよね。
そのうちに。
引数チェック
kzn さんの回答では引数をチェックしていらっしゃいます。
私は・・・引数のチェックを忘れました。
それで気付いたのだけど、私はほとんど引数をチェックしなかったりする。
何故だろう・・・と、考えてみた。
どのアプリケーションにも、入力と出力があります。コンピューターの内部でとじ込もっているだけでは何もなしていないに等しい。
入力は入り口で全て範囲をチェックします。範囲外だったときにどうするのかはアプリケーションそれぞれですが、ここで重要なのは範囲外のデータの侵入をできるだけブロックするということ。入り口以外のコードでは「範囲外はあり得ない」という想定で設計します。
以前に、全関数で漏れなく引数をチェックしているというコードを見たことがあります。一見、堅牢なようにも見えるのですが、全関数というのはさすがにあまりにも冗長です。アプリケーションは、入力を得てからその入力データを使う関数に到達するまでに数関数経由することは少なくありません。
func1(入力)→func2→func3→func4→func5(使用)
この、「func1」から「func5」まで、全ての関数で同じ引数を同じ範囲でチェックするのは生産性を下げることしか意味がないのではないかとさえ思えるほどです。いや、もしかしたら、「func3」のチェックが間違っていたら、品質の低下にもつながりかねません。コードは長ければ長いほど、当然ながら不具合が混入する可能性も増大します。書かなくていいものなら書かないに越したことはありません。
とは言うものの。
今回の関数はもっと汎用的な位置付けです。
特定のアプリケーションを越えて、別のアプリケーションでも使用するかもしれない。こういう関数は引数チェックを入れた方がいい。
範囲外の引数を受け入れた場合、いっそアプリケーションが強制終了でもすればいいのだけど、なんとなく動いてしまうことって意外にあるんですね。そういう場合は実に不具合に気付きにくい。極端なときには「一年後に発覚した」なんてこともなくはありません。問題は早く見つけるに越したことはありません。引数をチェックしてしっかり捉えましょう。
日本語と文字コード
Akio van der Meer さんは日本語についてもテストしていらっしゃいます。
日本語文字コードには、いい思い出が、ない。
できれば避けて通りたい。
という心理が働いたのかどうかは自分でも定かではないのですが、日本語のテストを忘れました。
私がソフトウェアの開発を始めたころ、まだ日本語は入力できませんでした。コードのコメントに日本語を書くこともできなかった。
そのうちに日本語が入力できるようにはなったけど、以来、プログラミングにおける「日本語」は、かなり右往左往しています。
最初は JISコード かな?
次が SJIS (シフトJIS)?
そして、 EUC 。
この辺りで既に混乱状態で、別にコーディングしたソースコードを UNIX にもっていこうとしたら文字コードを変換しなければいけない。既に EUC に変換したのをまた EUC に変換してしまったのかと思われるようなわけのわからない状況になってしまったコード(要するに、既に日本語コメントが文字化け状態でまったく読めないコード)もありました。
また、別の時には・・・。
確か、コメントに次のように書いたコードがありました。
/*ナントカナントカ可能*/
で。
この「能」の文字。
EUC コードでは「0x94 0x5C」なんですね。
さらにこの「0x5C」なんですが、ASCIIコードでは「\」(バックスラッシュ)。この「バックスラッシュ」なんですが、C言語ではエスケープシーケンスになります。その頃は、コンパイラもいささか貧相で、「\」をエスケープシーケンスに解釈しちゃって、「*/」でコメントが終わらないということもありました。そのせいで、未だに「 */」のように、コメント終端の前に半角の空白を入れる習慣があります(笑)。
その後さらに、 UTF-8 だの、 UTF-16 だの、もう、いろいろでクタクタになります。
ちなみに、C言語の標準関数にも「wcslen」など、ワイド文字に対応した関数も出てきています。この辺りの使い分けも面倒。
むむ。
愚痴ばかりになってしまった。
一応、追加でテストしてみました。
{
char* s = "日本語";
char* _s;
_s = salloc(s);
printf("0x%016lx %s\n", (unsigned long)s, s);
printf("0x%016lx %s\n", (unsigned long)_s, _s);
printf("\n");
sfree(_s);
}
0x0000005a42f97630 日本語
0xb4000072f155a650 日本語
ふーん、みたいに動いた。
ちょっとつまらないんだけど(笑)。
ちなみに。
一文字3byteを使うのは「UTF-8」です。
「UTF-8」は文字毎にサイズが変わるらしいので、ややこしいことこの上ない。文字数からバイト数は予想困難。標準関数に頼るしかないことになります。
0 か NULL か \0 か
どれも、要するに 0 という数値です。
それなりに意味があるんだろうけど、どうも、正確に把握してる人っているんかしらと時々思う。
そもそも、私からしてアヤシイ。
動いてしまうと考えずにそのまま放置してしまう。
C言語って、そんなところがある。
その時に問題なくても、別のコンパイラや別のCPUに移植しようとしたときに問題になるかもしれない。
今回は、 kzn さん、Akio van der Meer さんのコードも拝見して、とても楽しかった。
誰かとやり取りして指摘されると、勉強になるのはもちろんですが、なにより楽しいですね。
kzn さん、Akio van der Meer さん、これからもよろしくお願いします。
次回も楽しみにしてますね~。
楽しいプログラミングを。
この記事が気に入ったらサポートをしてみませんか?