일하다가 경험한 일이다. 동료가 실행이 이상하다며 같이 보자고 했다. bool 변수 하나가 visual studio watch 에서는 true 값을 갖는데 if 문에서 true 와 비교하는 쪽으로 가지 않았다. 예를 들면
void function(bool isValid) { //isValid : IDE의 watch 창에서는 true 였다. if (isValid == true) { } else { // 이곳으로 온다!!! } }
리빌드를 해봐도 IDE를 재시작해도 같은 결과였다. 혹시나 하여 함수에 bool 값을 넘긴 callstack을 따라 가며 watch를 봤지만1 계속 true 였다. 직접 이 1byte bool 변수의 메모리 값을 봤고 매우 놀랐다. 1 이나 0이 아니다!
보통, bool 변수에는 어떤 판정의 결과를 저장한다. int*변수 p 를 bool 변수 isNotNull 에 저장한다면 경고 2 가 발생하겠지만 p의 nullptr 여부에 따라 isNotNull이 true 또는 false 값을 갖는다.
000000007FFF3B3A cmp qword ptr [p],0 000000007FFF3B40 je wmain+49h (7FFF3B49h) 000000007FFF3B42 mov byte ptr [rsp+50h],1 000000007FFF3B47 jmp wmain+4Eh (7FFF3B4Eh) 000000007FFF3B49 mov byte ptr [rsp+50h],0 000000007FFF3B4E movzx eax,byte ptr [rsp+50h] 000000007FFF3B53 mov byte ptr [isNotNull],al p가 0이면 7FFF3B49h 로 점프, 1이면 7FFF3B42로 점프 각각 점프 한 다음 rsp+50h가 가리키는 영역에 0 또는 1을 저장. 이것을 eax 에 다시 저장한 후 isNotNull에 저장한다. (visual studio 2010 sp1, x64, debug build)
하지만 bool 값이 네트워크 버퍼에서 가져온 값이라면 어떨까? 1byte 를 읽어서 어떤 bool 변수 x에 강제로(?) 넣는다면, x 가 다른 bool 변수에 전달하는 값은 true or false 값이 아니다. x에 강제로 넣어진 값이 계속 전달된다. 아래는 이를 표현하고자 만들어본 코드다.
void function(bool result) { if (result == true) { cout<<"hello"<<endl; } else { cout<<"world!"<<endl; } } int _tmain(int argc, _TCHAR* argv[]) { bool isValid; memset(&isValid, 11, sizeof(bool)); bool next = isValid; function(next); return 0; }
16번 라인에 의해 isValid 영역에는 11값이 저장된다. 이를 next에 다시 대입하지만 18번 라인은 어셈블리 코드로
000000007FFF3B36 movzx eax,byte ptr [isValid] 000000007FFF3B3B mov byte ptr [next],al
이것 뿐이기 때문에 값을 복사만 한다. 하여 11 값을 갖는 isValid는 next 에 같은 값을 복사 result에도 11값을 넘겨준다. result는 3번라인에서 true 가 아니기 때문에 9번 라인으로 진행된다.
문제를 찾기 힘들었던 것 중 하나는 위의 코드를 예를 들자면 watch 창에서 result 값이 true 로 표시된 것이었다. 아마도 watch 에서는 값을 표시할 때 bool 변수의 값을 보여줄 때는 1byte의 값이 0이 아니면 true 로 표시하고, 0이면 false 로 표시하는 것으로 추정된다. 0 도 1도 아닌 값이라고 표시해주면 좋았을 텐데 그게 좀 아쉽네. watch 에서 해당 변수의 실제 값을 보려면 watch 이름을 result 가 아니라
*((char*)(&result)),d
로 변경해주면 된다.