とにかくコードは書かないことだ。
■ある引数のデータを処理して返すとき、オーバーフローを防ぐためそれ以上の数値で処理する(勿論、防ぐとはいっても、引数のデータ範囲に依存する。大切なことは常に本当に問題か?どうかを確認すること。)
私の上司曰く、「とにかくコードは書かないことだ。書けば書いた分だけ不具合が生じる可能性がある。」
また、「コードも単純なほうが第3者にレビューするのもうれしいし、テストするときのも優しい。」だそうだ。
こうした”設計思想”を学べる立場にある新人、」というのは素晴らしいと思う(いつまでもこの立場ではいられないのだが)。しかしながら、じゃぶじゃぶと工数(時間、つまり会社のお金)を使ってはいけないのだ、ということを肝に銘じておかねばならない。
back up 中..... 符号ある・なし変数の混ざった計算の方法
◎危険なコード
typedef unsigned char UI_8; // 8 bit 符号付き変数で収まるデータ範囲 0 〜 255 typedef signed char SI_8; // 8 bit 符号付き変数で収まるデータ範囲 -127 〜 128 /* * UI_8を引数に、SI_8と絡ませ、結局UI_8 を返す関数 */ UI_8 function_sample(UI_8 data_arg) { SI_8 ret_v; // 戻り値:符号付き変数 ret_v= (SI_8) data_arg + s_offset; // 同ソース内 static変数 (SI_8) s_offsetの足し込み // 足し算をキャストして型変換させて…はOKだが、 //(UI_8)から(SI_8)等、小さい範囲へのキャストは //データ範囲もみつつ慎重にならなければならない // この場合、data_arg の129〜255 がつぶれてしまう。 return (UI_8)ret_v; //また、s_offset の値によってはオーバーフローする }
◎(修正後)
typedef unsigned char UI_8; // 8 bit 符号付き変数で収まるデータ範囲 0 〜 255 typedef signed char SI_8; // 8 bit 符号付き変数で収まるデータ範囲 -128 〜 127 typedef signed char SI_16; // 16 bit 符号付き変数で収まるデータ範囲 -32000 〜 32000 UI_8 function_sample(UI_8 data_arg) { SI_16 ret_v; // 戻り値:符号付き変数 ret_v= (SI_16)data_arg + s_offset; // s_offsetの足し込み(より大きな範囲なのでひとまず安心(*)) if ( ret_v < 0 ) { // (UI_8)に負数はないので ret_v = 0; // 戻り値を 0にする } return (UI_8)ret_v; // オーバーフローのしにくさがあがり、より安全な設計である }
(*) 「ひとまず」の懸念内容としては、これがコンパイラに依存するため、確認する必要がある、の意。