Debug「somebool==true」の罠
昨日デバッグ中に、次のような奇妙な問題に遭遇しました.
原文では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.
重点は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:
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つの処理です.
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つの処理です.