C++Builder 2010コレクションクラスの1つのBUGを発見

2687 ワード

今日C++Builder 2010で小さなコードを書いて、集合クラスを使いましたが、集合演算の結果がどうしても間違っていて、他の原因を排除して、最後に集合クラスのソースコードの問題であるべきだと確定しました.以下は集合クラスのテストコードです.
enum TTest{tt0, tt15 = 15, tt16 = 16};
typedef Set TTests;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	TTests t1 = TTests() << tt15;
	TTests t2 = TTests() << tt16;

	ShowMessage(t1.ToInt());	// 32768
	ShowMessage(t2.ToInt());	// 16777216
}

テストコードの集合クラス変数t 1,t 2はそれぞれ15ビット目と16ビット目として定義され、表示結果はそれぞれ32768と65536であるべきであり、t 1結果32768は正しいが、t 2表示は167777216であり、明らかにエラーである.
次にDelphi 2010で似たようなテストコードを書きました.
type
  TTest = (tt0, tt15 = 15, tt16 = 16);
  TTests = set of TTest;

procedure TForm1.Button1Click(Sender: TObject);
var
  t1, t2: TTests;
begin
  t1 := [tt15];
  t2 := [tt16];
  ShowMessage(IntToStr(LongWord(t1)));  // 32768
  ShowMessage(IntToStr(LongWord(t2)));  // 65536
end;

Delphi 2010コードは結果が完全に正しいことを示しています.
C++Builder 2010コードの集合クラス変数t 2が8ビット左にシフトしたことは明らかであり、すなわち167777216は24番目の位置ビット後の結果である.
C++Builder 2010コレクションクラスソースファイルsyssetを呼び出します.h、そのToInt関数を見て、問題がここにあることを発見しました.ソースコードのデータ定義とToInt関数コードを見てください.
  template
  class RTL_DELPHIRETURN SetBase
  {
  protected:
    unsigned char Data[((((int)(maxEl/8))-((int)(minEl/8))+1) != 3)?
       (((int)(maxEl/8))-((int)(minEl/8))+1): 4];
  };

  template
  class RTL_DELPHIRETURN Set : SetBase
  {

    ......

    int __fastcall ToInt(void) const
    {
  #pragma option push -w-inl
    int Result = 0;
    for (int i = sizeof(Data)-1; i >= 0; i--)
    {
      Result |= Data[i];
      Result <<= (i * sizeof(unsigned char) * 8);
    }
    return Result;
  #pragma option pop
    }

    ......

  };

上のテスト用の集合クラスTTests最小メンバー値=0、最大メンバー値=16は、ソースコードデータ長定義式で3、Byteタイプの配列Data長=4と計算され、テストコードの集合クラス変数t 1とt 2のデータはそれぞれ「0x 80000」と「0010」であり、ToInt関数変換時には、t 1の有効開始にi=1、Resultに0 x 80を付与した後、合計2回8ビット左にシフトした.結果は0 x 8000(32768)であり、t 2の有効開始はi=2であり、Result賦値1の後に3回左に移動して24ビット(すなわち2*1*8=16、1*1*8=8、0*1*8=0)であり、結果は0 x 100000(16777216)であった.
ソースコードのToInt関数は次のように変更できます.
    int __fastcall ToInt(void) const
    {
  #pragma option push -w-inl
    int Result = 0;
    for (int i = sizeof(Data)-1; i >= 0; i--)
    {
      Result |= Data[i];
      if (i)
          Result <<= (sizeof(unsigned char) * 8);
    }
    return Result;
  #pragma option pop
    }

私はC++Builder 2010しか見ていません.また、ToInt関数にも関連しています.他のバージョンや関数に問題があるかどうかは分かりません.明らかにこのBUGはプログラムの中で致命的な可能性があります.C++Builderプログラマーの皆さん、警戒してください.