Debug「somebool==true」の罠


昨日デバッグ中に、次のような奇妙な問題に遭遇しました.
class SomeClass
{
public:
    int SomeMethod();
private:
    bool _someBool;
};

int SomeClass::SomeMethod()
{
    int someInt;

    //1
    someInt = _someBool ? 5 : 2;
    cout << "someInt=" << someInt << endl; //5

    //2
    someInt = (_someBool == true) ? 5 : 2;
    cout << "someInt=" << someInt << endl; //2

    return someInt;
}

int _tmain(int argc, _TCHAR* argv[])
{
    SomeClass sc;
    int r = sc.SomeMethod();

	return 0;
}

原文ではcase 1と書く形式です.Debug buildではsomeInt=5ですがrelease buildではsomeInt=0です.Windowsでは毎回再現されるのではなく、Linuxでは100%再現されます.Windowsで直接debug SomeMethod、VS intelligence表示を発見_someBoolはtrueです.
 
Registersウィンドウで次のように表示されます.
EAX = CCCCCCCC EBX = 7EFDE000 ECX = 0025F867 EDX = 00000001 ESI = 00000000 EDI = 0025F780 EIP = 01131DB3 ESP = 0025F69C EBP = 0025F780 EFL = 00000212
 
Watchウィンドウ:
2つのことを見ました
1. _someBoolはbool型でtrueと表示されますが、その16進数表示は0 xccです(前に見たのは0 xcdで、初期化されていないメモリを表しています)
----追跡しなかったため_someBoolはどのように来たのか(16進数値も見ていない)、直接この方法でdebugされ、VSに誤導されたのですが、なぜtrueなのに0を_someIntに返すのでしょうか?!!理由は、_someBoolを定義しても初期化されていないので、値はundefinedの、メモリの中のあるデータでしょう(私はずっと値をつけないと思っていましたが、デフォルトはfalse==).VSは何をtrueと言いますか.VSはそれを0と判断しているので、0に等しくなければtrueだと思います.
2. _someBoolのメモリアドレスはECXレジスタに格納される.
 
ブレークポイントが1の文になったとき、go to disassembly.
    someInt = _someBool ? 5 : 2;
010C1DB3  mov         eax,dword ptr [this] 
010C1DB6  movzx       ecx,byte ptr [eax] 
010C1DB9  neg         ecx  
010C1DBB  sbb         ecx,ecx 
010C1DBD  and         ecx,3 
010C1DC0  add         ecx,2 
010C1DC3  mov         dword ptr [someInt],ecx 

重点はneg,sbbの2つの文です.ECXは_someBoolの値.
neg r:0-r->rという意味で、ゼロ以外の数を減算すると、CF(Carry Flag)ビットが設定されている借方が発生します.本例による、ecxは本来1であり、実行後、-1、CF=1である.
sbb r,r:r-r-CF->rという意味で、rを0または-1に設定します.この例によれば、実行後、ecxは−1、すなわちFFFFFFFFとなる.したがって,最初rが0であれば,ここまでも=0,0でなければここまで=1である.
その後のand,addはecxの値に基づいて,someInt賦課.
このとき「?:」という三元付与文の条件判断は,0と比較していることがわかる.someBool値は0ではないのでtrueとして5を返します.
 
ブレークポイント2:
    someInt = (_someBool == true) ? 5 : 2;
010C1E06  mov         eax,dword ptr [this] 
010C1E09  movzx       ecx,byte ptr [eax] 
010C1E0C  sub         ecx,1 
010C1E0F  neg         ecx  
010C1E11  sbb         ecx,ecx 
010C1E13  and         ecx,0FFFFFFFDh 
010C1E16  add         ecx,5 
010C1E19  mov         dword ptr [someInt],ecx 

sub r,1:ecx値-1を先にしてnegとsbbの演算をします.この時は1と比較しています.つまり、trueの値=1です._someBoolの値は1ではないのでfalseで2を返します.
 
まとめ:
1.メンバー定義には初期デフォルト値が割り当てられます.
2.VS判定boolは、0と比較し、=0ではなくtrueとなる.
3.ブール型trueの値は1、「?:」の3元演算子の2つの処理です.