Microsoft MVP성태의 닷넷 이야기
VC++: 96. C/C++ 식 평가 - printf("%d %d %d\n", a, a++, a); [링크 복사], [링크+제목 복사],
조회: 23297
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

C/C++ 식 평가 - printf("%d %d %d\n", a, a++, a);

트위터에서 재미있는 글을 보았습니다. ^^

cpp_inc_1.png

#include <stdio.h>

int main()
{
 int a = 5;
    
 printf("%d %d %d\n", a, a ++, a);
 return 0;
}

저도 예상한 답이 틀렸습니다. ^^ printf가 __cdecl 호출 규약을 따르기 때문에 함수의 인자는 우측부터 전달하게 되어 식 평가가 가장 마지막 인자인 a가 먼저 전달되고, 두번째로 a ++가 평가되어 6으로 증가한 값이 첫 번째 a로 할당되어 "6 5 5"가 나올 거라 예상한 것입니다.

왜 "6 5 6" 이 나왔는지는 어셈블리 코드를 통해 유추할 수 있습니다.

    printf("%d %d %d\n", a, a++, a);
010417D5 8B 45 F8             mov         eax,dword ptr [a]  
010417D8 89 85 30 FF FF FF    mov         dword ptr [ebp-0D0h],eax  // [ebp-0D0h]의 값에 초기 값 5를 보관
010417DE 8B 4D F8             mov         ecx,dword ptr [a]  
010417E1 83 C1 01             add         ecx,1  // ecx == 6
010417E4 89 4D F8             mov         dword ptr [a],ecx  
010417E7 8B 55 F8             mov         edx,dword ptr [a]  
010417EA 52                   push        edx    // ecx의 값이 전달되었으므로 6
010417EB 8B 85 30 FF FF FF    mov         eax,dword ptr [ebp-0D0h]  
010417F1 50                   push        eax    // [ebp-0d0h]의 값이 전달되었으므로 5
010417F2 8B 4D F8             mov         ecx,dword ptr [a]  
010417F5 51                   push        ecx    // ecx의 값이 전달되었으므로 6
010417F6 68 30 6B 04 01       push        offset string "%d %d %d\n" (01046B30h)  
010417FB E8 20 FB FF FF       call        _printf (01041320h)  

재미있군요. 그래도 전 저렇게 코드를 생산한 것이 Visual C++의 독자적인 식 평가라고 생각했습니다. 하지만, 온라인 컴파일러를 통해 GCC에서 테스트 해보니,

Code, Compile & Run
; https://www.codechef.com/ide

역시 같은 결과가 나왔습니다. Visual C++과 GCC 컴파일러가 동일한 결과를 낸 걸로 봐서 어떤 규칙이 있을 듯한데, 일단 결과만 놓고 억지로 해석해 보면 이럴 것 같습니다.

1. printf로 전달되는 인자의 수식이 평가됩니다.
2. 즉, 첫 번째 a는 평가할 것이 없으므로 코드 출력이 안되고,
3. a++는 a 값을 보존한 다음에 1을 증가시키고
4. 마지막 a도 평가할 것이 없으므로 코드 출력이 안됨.

5. 인자에 대한 식 평가는 끝났고 이제 값을 스택으로 전달하는데,

6. 마지막 a는 현재 변수 a에 보존된 값을 그대로 전달.
7. 중간의 a++ 인자는 수식이 평가된 것이므로 보존된 값을 전달.
8. 첫 번째 a도 현재 변수 a에 보존된 값을 그대로 전달.

뭐... 그냥 제 나름대로의 해석일 뿐입니다. 혹시 정확하게 아시는 분은 덧글 부탁드리겠습니다. ^^

참고로, Visual C++의 릴리스 모드로 컴파일 시에는 "5 5 5" 출력 결과를 낸다는 차이점이 있습니다.

    int a = 5;

    printf("%d %d %d\n", a, a++, a);
00D61000 6A 05                push        5  
00D61002 6A 05                push        5  
00D61004 6A 05                push        5  
00D61006 68 08 21 D6 00       push        offset string "%d %d %d\n" (0D62108h)  
00D6100B E8 20 00 00 00       call        printf (0D61030h)  
00D61010 83 C4 10             add         esp,10h  

반면, GCC에서는 최적화 모드에서도 "6 5 6"으로 동일하게 나왔습니다.

C/C++ 스펙에 정확하게 명시하고 있지 않다면, 이건 어느 쪽의 버그다라고 규정짓기 힘듭니다. 따라서, 이런 식의 코드는 웬만하면 개발자가 명시적으로 지정하는 것이 좋을 것입니다. ^^

그러고 보니, 프로그래밍 언어는 결과를 예측하기 힘든 경우가 종종 있는 것 같습니다. 마치 SSE 지원 유무에 따라 결과가 바뀌었던 다음의 글이 그랬던 것처럼.

C# - 부동소수 계산 왜 이렇게 나오죠? (2)
; https://www.sysnet.pe.kr/2/0/10873




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 4/28/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  127  128  129  130  [131]  132  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
1780정성태10/15/201424178오류 유형: 249. The application-specific permission settings do not grant Local Activation permission for the COM Server application with CLSID
1779정성태10/15/201419706오류 유형: 248. Active Directory에서 OU가 지워지지 않는 경우
1778정성태10/10/201418156오류 유형: 247. The Netlogon service could not create server share C:\Windows\SYSVOL\sysvol\[도메인명]\SCRIPTS.
1777정성태10/10/201421257오류 유형: 246. The processing of Group Policy failed. Windows attempted to read the file \\[도메인]\sysvol\[도메인]\Policies\{...GUID...}\gpt.ini
1776정성태10/10/201418302오류 유형: 245. 이벤트 로그 - Name resolution for the name _ldap._tcp.dc._msdcs.[도메인명]. timed out after none of the configured DNS servers responded.
1775정성태10/9/201419429오류 유형: 244. Visual Studio 디버깅 (2) - Unable to break execution. This process is not currently executing the type of code that you selected to debug.
1774정성태10/9/201426624개발 환경 구성: 246. IIS 작업자 프로세스의 20분 자동 재생(Recycle)을 끄는 방법
1773정성태10/8/201429781.NET Framework: 471. 웹 브라우저로 다운로드가 되는 파일을 왜 C# 코드로 하면 안되는 걸까요? [1]
1772정성태10/3/201418569.NET Framework: 470. C# 3.0의 기본 인자(default parameter)가 .NET 1.1/2.0에서도 실행될까? [3]
1771정성태10/2/201428080개발 환경 구성: 245. 실행된 프로세스(EXE)의 명령행 인자를 확인하고 싶다면 - Sysmon [4]
1770정성태10/2/201421689개발 환경 구성: 244. 매크로 정의를 이용해 파일 하나로 C++과 C#에서 공유하는 방법 [1]파일 다운로드1
1769정성태10/1/201424108개발 환경 구성: 243. Scala 개발 환경 구성(JVM, 닷넷) [1]
1768정성태10/1/201419531개발 환경 구성: 242. 배치 파일에서 Thread.Sleep 효과를 주는 방법 [5]
1767정성태10/1/201424632VS.NET IDE: 94. Visual Studio 2012/2013에서의 매크로 구현 - Visual Commander [2]
1766정성태10/1/201422486개발 환경 구성: 241. 책 "프로그래밍 클로저: Lisp"을 읽고 나서. [1]
1765정성태9/30/201426054.NET Framework: 469. Unity3d에서 transform을 변수에 할당해 사용하는 특별한 이유가 있을까요?
1764정성태9/30/201422288오류 유형: 243. 파일 삭제가 안 되는 경우 - The action can't be comleted because the file is open in System
1763정성태9/30/201423856.NET Framework: 468. PDB 파일을 연동해 소스 코드 라인 정보를 알아내는 방법파일 다운로드1
1762정성태9/30/201424550.NET Framework: 467. 닷넷에서 EIP/RIP 레지스터 값을 구하는 방법 [1]파일 다운로드1
1761정성태9/29/201421577.NET Framework: 466. 윈도우 운영체제의 보안 그룹 이름 및 설명 문자열을 바꾸는 방법파일 다운로드1
1760정성태9/28/201419841.NET Framework: 465. ICorProfilerInfo::GetILToNativeMapping 메서드가 0x80131358을 반환하는 경우
1759정성태9/27/201430977개발 환경 구성: 240. Visual C++ / x64 환경에서 inline-assembly를 매크로 어셈블리로 대체하는 방법파일 다운로드1
1758정성태9/23/201437884개발 환경 구성: 239. 원격 데스크톱 접속(RDP)을 기존의 콘솔 모드처럼 사용하는 방법 [1]
1757정성태9/23/201418410오류 유형: 242. Lync로 모임 참여 시 소리만 들리지 않는 경우 - 두 번째 이야기
1756정성태9/23/201427412기타: 48. NVidia 제품의 과다한 디스크 사용 [2]
1755정성태9/22/201434202오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
... 121  122  123  124  125  126  127  128  129  130  [131]  132  133  134  135  ...