성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Visual C++ 배열 초기화를 위한 기계어 코드 확인</h1> <p> 재미있는 논의가 있었군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > char* 문자열 버퍼 초기화의 내부 ; <a target='tab' href='http://www.gamedevforever.com/188'>http://www.gamedevforever.com/188</a> </pre> <br /> 내용은 대충 이렇습니다. 다음과 같이 스택상에 생성되는 배열을 정의한 경우,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > char temp[10] = { 0 }; // 1번 char temp[10] = { 1, 2, }; // 2번 </pre> <br /> 1번의 경우에는 배열 요소가 모두 0으로 초기화 되는 반면, 2번의 경우에는 처음 두 번째 배열 요소만 각각 1, 2로 초기화 되고 이후의 경우에는 0으로 초기화 된다는 것입니다.<br /> <br /> 그런데, 이 초기화에 대한 방법이 본문과 덧글에서 다양하게 설명이 되고 있는데요.<br /> <br /> <ol> <li>memset으로 초기화 된다.</li> <li>{ , , } 안에 들어가는 숫자는 0으로 초기화 후에 해당 숫자로 셋팅되는 듯 하다.</li> <li>temp[0] = 1, temp[1] = 2가 먼저 이뤄지고 temp[2] ~ temp[9]까지는 4byte씩 0으로 초기화 된다.</li> <li>char temp[10] = {0, }; 컴파일 후 memset을 호출하는 이유는 컴파일러가 컴파일하면서 코드를 바꾸는 듯.</li> </ol> <br /> 위의 4가지 설명은 아예 틀리는 것도 있고 절반만 맞는 경우도 있는데... 어쨌든 정확한 답은 아닙니다. 어디... 직접 확인을 해볼까요?<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이런 의문을 해결하는 가장 좋은 방법은 Visual Studio의 디버그 모드에서 "Go to Disassembly..." 메뉴를 이용하여 어셈블리 코드 창을 함께 띄워놓고 보는 것입니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='cpp_array_int_1.png' src='/SysWebRes/bbs/cpp_array_int_1.png' /><br /> <br /> 그럼, 답이 나올 텐데요. 문제는, 이것이 상황마다 초기화 방법이 틀리다는 것입니다.<br /> <br /> 우선, "char temp[3] = { 0 };" 코드에서부터 시작해 볼텐데요. 이는 Visual C++ 컴파일러에 의해서 다음과 같은 어셈블리 코드로 변환되는데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > mov byte ptr [ebp-18h],0 xor eax,eax mov word ptr [ebp-17h],ax </pre> <br /> C++ 코드로 정리해 보면 다음과 같은 효과를 갖습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > temp[0] = 0; *(short *)&temp[1] = 0; </pre> <br /> 그렇다면 혹시 짝수로 4개의 char 요소를 가지면 어떻게 초기화 될지 예상이 되시나요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > char temp[4] = { 0 }; mov byte ptr [ebp-18h],0 xor eax,eax mov word ptr [ebp-17h],ax mov byte ptr [ebp-15h],al </pre> <br /> 역시 C++ 코드로 정리하면 대충 이런 형태입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > temp[0] = 0; *(short *)&temp[1] = 0; temp[3] = 0; </pre> <br /> 개인적으로는, int로 형변환해서 4바이트를 한꺼번에 초기화할 줄 알았는데 ^^ 예상이 틀렸습니다. 어쨌든 이런 식으로 배열 크기가 5바이트부터 초기화 되는 기계어 코드를 유사한 C++ 코드로 바꿔보면 다음과 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>char temp[5] = { 0 };</span> ==> C++ 코드 temp[0] = 0; *(int *)&temp[1] = 0; <span style='color: blue; font-weight: bold'>char temp[6] = { 0 };</span> ==> C++ 코드 temp[0] = 0; *(int *)&temp[1] = 0; temp[5] = 0; <span style='color: blue; font-weight: bold'>char temp[7] = { 0 };</span> ==> C++ 코드 temp[0] = 0; *(int *)&temp[1] = 0; *(short *)&temp[5] = 0; <span style='color: blue; font-weight: bold'>char temp[8] = { 0 };</span> ==> C++ 코드 temp[0] = 0; *(int *)&temp[1] = 0; *(short *)&temp[5] = 0; temp[7] = 0; <span style='color: blue; font-weight: bold'>char temp[9] = { 0 };</span> ==> C++ 코드 temp[0] = 0; *(int *)&temp[1] = 0; *(int *)&temp[5] = 0; </pre> <br /> 위의 결과만 보면, 배열 초기화는 memset으로 된다는 말은 전혀 맞지 않는 것 같습니다. 그럴까요? 위의 변환 과정을 보고 눈치채실 분도 계시겠지만 배열이 커지는 경우 최대 4바이트씩 초기화되는 *(int*)&temp[...] = 0 코드가 계속 늘어나는 것이 분명 비효율적이라는 것을 알 수 있습니다. 그렇죠? ^^ 따라서 어느 배열 크기 이후로는 함수 호출로 대체될 텐데요. 개인적으로 테스트하기에는 배열의 크기가 40을 넘어서면서 그렇게 바뀌는 것을 확인할 수 있었습니다.<br /> <br /> 그런데, 이 결과가 또한 재미있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > char temp1[40] = { 0 }; mov byte ptr [ebp-50h],0 push 27h push 0 lea eax,[ebp-4Fh] push eax call @ILT+1045(_memset) (0DB141Ah) add esp,0Ch </pre> <br /> 어떠세요? 이에 대해 C++ 코드로 다시 변형해 보면, 다음과 같은 코드가 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > temp[0] = 0; memset(&temp[1], 0, 0x27); </pre> <br /> 아마도 (저를 포함해서) 대부분의 사람들이 단순하게 "memset(temp, 0, 0x28)"을 예상했을 텐데 ^^ 아쉽게도 Visual C++ 컴파일러는 위와 같이 이상하게 변환을 해주었습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 사실, 위의 결과도 경우에 따라 다시 달라지는데요. 예를 들어, char 형이 아니라 int 형 배열이라면 어떻게 될까요? int는 4바이트라서 DWORD 크기에 딱 맞기 때문에 다음과 같은 식으로 초기화가 이뤄지다가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > mov dword ptr [ebp-70h],0 xor eax,eax mov dword ptr [ebp-6Ch],eax ...[이하 남은 배열 수만큼 반복]... </pre> <br /> 배열 크기가 12가 되는 순간부터, memset을 사용하는 코드로 바뀌었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int temp1[12] = { 0 }; mov dword ptr [ebp-74h],0 push 2Ch push 0 lea eax,[ebp-70h] push eax call @ILT+1045(_memset) (0FE141Ah) add esp,0Ch ==> C++ 코드 temp[0] = 0; memset(&temp[1], 0, 11 * sizeof(int)); </pre> <br /> char 배열에서처럼, int 배열에서도 역시 첫 번째 배열 요소에 대해서는 값이 직접 입력되고, 나머지 배열 요소들은 memset에 전달되어 초기화 되었습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 마지막으로, 일부 배열 요소가 초기화된 경우는 어떤 코드로 컴파일이 될까요?<br /> <br /> 이에 대해서는 전체적인 규칙은 위에서 설명했던 것과 유사하게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > char temp2[3] = { 1, 2, }; mov byte ptr [ebp-68h],1 mov byte ptr [ebp-67h],2 xor eax,eax mov byte ptr [ebp-66h],al ==> C++ 코드 temp[0] = 1; temp[1] = 2; temp[2] = 0; char temp[4] = { 1, 2, }; mov byte ptr [ebp-70h],1 mov byte ptr [ebp-6Fh],2 xor eax,eax mov word ptr [ebp-6Eh],ax ==> C++ 코드 temp[0] = 1; temp[1] = 2; *(short *)(&temp[2]) = 0; </pre> <br /> 재미있는 것은 memset으로 바뀌는 시점의 배열 크기인데요. 처음 2바이트가 의미있는 코드를 가지기 때문에 이후 반복되는 코드 변환에서 누락되므로 40이 아닌 43 크기를 가지는 순간부터 memset으로 변환된다는 점입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이쯤에서 문제에 대한 결론을 내려 볼까요?<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> 배열 요소의 초기화 지정은 일정 수의 크기 이내에서는 0으로 직접 초기화를 하다가, 그 이후부터는 memset으로 바뀐다.<br /> </div><br /> <br /> '일정 수'라는 크기는 컴파일러마다 다를 수 있고, 심지어 컴파일러의 버전마다 내부적으로 정해진 규칙에 의해 바뀔 수 있으므로 그 정확한 숫자를 기억하는 것은 별로 의미가 없을 것입니다. (사실, 위의 결과는 64비트 버전에서는 또 다를 수 있습니다.)<br /> <br /> 개인적으로 위의 테스트를 진행하면서 예전에 써둔 다음의 글들이 생각났습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > WPF 이벤트에 속한 핸들러 확인 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/624'>http://www.sysnet.pe.kr/2/0/624</a> CLR JIT 컴파일러가 생성한 기계어 코드 확인하는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/975'>http://www.sysnet.pe.kr/2/0/975</a> </pre> <br /> 위에서도 확인되지만, 마이크로소프트는 '최적화'에 대해서 꽤나 신경을 쓰는 편이고... 사실 그것을 외부에서 정확히 '수치'적으로 정의하는 것은 다소 위험할 수 있습니다.<br /> <br /> 그러고 보니... 아래의 글까지 생각나는군요. ^^;<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > VC++에서 bool이 가지는 의미. ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/487'>http://www.sysnet.pe.kr/2/0/487</a> </pre> </p> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
3958
(왼쪽의 숫자를 입력해야 합니다.)