とにかくコードは書かないことだ。

■ある引数のデータを処理して返すとき、オーバーフローを防ぐためそれ以上の数値で処理する(勿論、防ぐとはいっても、引数のデータ範囲に依存する。大切なことは常に本当に問題か?どうかを確認すること。)

私の上司曰く、「とにかくコードは書かないことだ。書けば書いた分だけ不具合が生じる可能性がある。
また、「コードも単純なほうが第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;                    // オーバーフローのしにくさがあがり、より安全な設計である
}

(*) 「ひとまず」の懸念内容としては、これがコンパイラに依存するため、確認する必要がある、の意。