トップ «前の日記(2006-04-23) 最新 次の日記(2006-04-25)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2006-04-24

_ VC6SP3だと思う

以前、こういうバグを書いたことがある。
/**
 * list 文字列のリスト
 * index 印字したい行のインデックスを指定する。listのサイズ
 *    以上を指定した場合は、最後の行を指定したものとして扱う。
 */
void print(std::list& list, int index)
{
    std::list::iterator it = list.begin();
    int max = (index > list.size()) ? list.size() : index;
    for (int i = 0; i < max; i++, it++) ;
    printf("%s?n", (*it).c_str());
}

見ての通りのバギーなプログラムだけど、記憶に間違いがなければVC6のSP3以前のどれかのバージョンではこれで正しく(仕様通りに)動いたのであった。そのために、少しも気づかなかった。

追記:相当にうそ臭い。もともとちゃんと動いていなかった可能性のほうが高い。文字列を出力するような目に見える処理じゃなかったからだ。

でも、別口からこのソースがVC5でコンパイルされて出荷されて、時々ハングしたり、時々クラッシュしたり、それは大惨事。

思い込みというのは恐ろしいもので、ここにバグがあるなんて考えてもいなかったから、えらく解決は長引いた。

問題は、僕の環境ではコンパイラが妙な最適化をしていたことにある。(もちろん、一番の問題はこのプログラムの明白なバグなわけだけど)

今のVC6ではどうオプションを変えても次のようなコードになる。

  mov	 eax, DWORD PTR [eax]
  dec	 ecx
  jne	 SHORT $L9827

しかし、記憶によれば

  dec	 ecx
  jne	 SHORT $L9827
  mov	 eax, DWORD PTR [eax]

とコンパイルされていたのだ。これは条件がヒットすれば1回eaxに対するmovがスキップできるから効率は良いかも知れない。でも、for文のコンパイルとしては間違いだ。

もしかしたら、それ自身が記憶違いで、そんなばかなコンパイルはされていなくて、たまたまstlの実装のend()の見方の問題だったのかも知れないし、すべてが僕の白昼夢だったのかも知れない。

VC6がどうコンパイルしたかはおいておくとして、この最初のリストのようなバグに対してはどういう方法で回避策がありえるだろうか?

list::end()に対するc_strの呼び出しで例外というのはきれいだが、あいにくそういう仕様ではない。したがって、コードで解決しなければならない。

たとえばコーディング規約ではどういう対応の仕方があるのだろうか?

あるいは、もっとうまい記述方法があるだろうか? ――もちろん、ある。list::size()の1/2によって++と--に分ける方法だ。この方法であれば、それぞれ上限および下限から真ん中へ向かって進むので境界を越えることはありえない。でも、コードは多少複雑になる。しかしバグも入りにくくかつ効率も良い。したがってそういう方法を記述させるようにすることが解決策のひとつだ。

他にはどんな方法があるかな?

追記:

for文の主題はiteratorであるべきで、 intにすべきではない

そのとおりですね。多分(その時に考えたこと)ブロックを2つ書きたくなかったとか、returnは1個所(最近、この呪縛からは解放されつつあるけど、結構縛られている面もある)とか、大して効果もないのに、list::end()と比較するよりintの大小比較のほうが早いとか、いろいろな(今にして思えばろくでもない)背景から↑を書いたようです。

#最後にbreakが欲しいかも


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|

ジェズイットを見習え