C++Builder 2010コレクションクラスの1つのBUGを発見
2687 ワード
今日C++Builder 2010で小さなコードを書いて、集合クラスを使いましたが、集合演算の結果がどうしても間違っていて、他の原因を排除して、最後に集合クラスのソースコードの問題であるべきだと確定しました.以下は集合クラスのテストコードです.
テストコードの集合クラス変数t 1,t 2はそれぞれ15ビット目と16ビット目として定義され、表示結果はそれぞれ32768と65536であるべきであり、t 1結果32768は正しいが、t 2表示は167777216であり、明らかにエラーである.
次にDelphi 2010で似たようなテストコードを書きました.
Delphi 2010コードは結果が完全に正しいことを示しています.
C++Builder 2010コードの集合クラス変数t 2が8ビット左にシフトしたことは明らかであり、すなわち167777216は24番目の位置ビット後の結果である.
C++Builder 2010コレクションクラスソースファイルsyssetを呼び出します.h、そのToInt関数を見て、問題がここにあることを発見しました.ソースコードのデータ定義とToInt関数コードを見てください.
上のテスト用の集合クラス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関数は次のように変更できます.
私はC++Builder 2010しか見ていません.また、ToInt関数にも関連しています.他のバージョンや関数に問題があるかどうかは分かりません.明らかにこのBUGはプログラムの中で致命的な可能性があります.C++Builderプログラマーの皆さん、警戒してください.
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プログラマーの皆さん、警戒してください.