인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

움직이는YM님의 프로필 이미지
움직이는YM

작성한 질문수

[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문

지역변수와 값 전달에서 질문

작성

·

223

0

지역변수와 값 전달부분에서 스택 프레임과 관련된 질문이 있습니다.

void IncreaseHp(int hp)

{

int finalHp = hp + 1;

hp = hp + 1;

}

int main()

{

int hp = 1;

int localValue = 1;

cout << "increase 호출 전 : " << hp << endl;

IncreaseHp(hp);

// 사실 이건 IncreaseHp(1); 이거다. 인수 값을 복사해서 넘기고 있다 이게 중요한 사실

cout << "Increase 호출 후 : " << hp << endl;

return 0;

}

이 코드에 디스 어셈블리를 사용하면

int hp = 1;

00142658  mov         dword ptr [hp],1  

int localValue = 1;

0014265F  mov         dword ptr [localValue],1  

cout << "increase 호출 전 : " << hp << endl;

00142666  mov         esi,esp  

00142668  push        offset std::endl<char,std::char_traits<char> > (01412A8h)  

0014266D  mov         edi,esp  

0014266F  mov         eax,dword ptr [hp]  

00142672  push        eax  

00142673  push        offset string "increase \xc8\xa3\xc3\xe2 \xc0\xfc : " (0149B30h)  

00142678  mov         ecx,dword ptr [__imp_std::cout (014D0D8h)]  

0014267E  push        ecx  

0014267F  call        std::operator<<<std::char_traits<char> > (014120Dh)  

00142684  add         esp,8  

00142687  mov         ecx,eax  

00142689  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (014D0A0h)]  

0014268F  cmp         edi,esp  

00142691  call        __RTC_CheckEsp (0141285h)  

00142696  mov         ecx,eax  

00142698  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (014D0A4h)]  

0014269E  cmp         esi,esp  

001426A0  call        __RTC_CheckEsp (0141285h)  

IncreaseHp(hp);

001426A5  mov         eax,dword ptr [hp]  

001426A8  push        eax  

001426A9  call        IncreaseHp (0141370h)  

001426AE  add         esp,4  

// 사실 이건 IncreaseHp(1); 이거다. 인수 값을 복사해서 넘기고 있다 이게 중요한 사실

cout << "Increase 호출 후 : " << hp << endl;

001426B1  mov         esi,esp  

001426B3  push        offset std::endl<char,std::char_traits<char> > (01412A8h)  

001426B8  mov         edi,esp  

001426BA  mov         eax,dword ptr [hp]  

001426BD  push        eax  

001426BE  push        offset string "Increase \xc8\xa3\xc3\xe2 \xc8\xc4 : " (0149B48h)

이런 값이 나옵니다.

위 어셈블리어에서

00142658  mov         dword ptr [hp],1 를 통해 main 에서 hp에 1을 복사대입해주고

IncreaseHp(hp);

001426A5  mov         eax,dword ptr [hp]  를 통해 hp = 1을 eax 레지스터에 복사대입해줍니다

001426A8  push        eax  를 통해 eax를 스택에 넣습니다.  

여기서 질문 있습니다.

1. 지금 스택에 들어간 eax(값은 1) 이건 main의 매개변수 인건가요? 아니면 IncreaseHp 함수의 매개변수hp인가요?

강의 설명에 따르면 매개변수와 반환 주소값은 이전 함수가 정해준다고 했으니 main이 IncreaseHP 함수의 매개변수를 정해준 것 같지만

IncreaseHP 함수의 매개변수가  맞는 생각인지 궁금합니다.

2.

001426A9  call        IncreaseHp (0141370h)   => 여기서 call을 통해 IncreaseHp  함수로 넘어가려는 것을 알았습니다.

이 때 강의 설명에 따르면 IncreaseHp에서 작업이 끝나면 돌아올 수 있게 반환 주소 값이 저장될 것입니다.

여기서 궁금한점이 그럼 main의 지역함수인 hp와 localValue는 언제 스택에 쌓이게 되는건가요?

어셈블리어로 CALL을 때리는 순간 돌아올 주소를 스택에 저장하고 IncreaseHp 함수 속으로 넘어가게 되는데

그렇다면  main의 지역변수는 스택에 쌓이지 못하게 되는 것 아닌가요?

(마찬가지로 만약 func1()안에 func2()가 있다면 func1()의 지역변수 또한 쌓이지 못하고 CALL을 만나는 순간

func2()로 넘어가는 것 아닌가요?)

첨부한 스크린샷을 보면 main()에서 매개변수 -> 반환 주소 값 -> 지역변수를 스택에 쌓고

func1()으로 넘어가는 것처럼 묘사가 되어있는데 이게 어떻게 가능한건지 궁금합니다..

3.

우리 main 함수는  IncreaseHp와 다르게 매개변수가 없으니까 스택에 쌓이는  main 부분은

반환 주소값(뭔가 return이 있으니 메인도 반환될 것 같습니다) -> 지역변수(2번 질문에 따르면 언제 쌓이는지 모르겠지만) 

이런식으로 2개의 값만 가지게 되는건가요?

답변 2

1

Rookiss님의 프로필 이미지
Rookiss
지식공유자

우선 어셈블리 코드에서 std::basic_ostream<char,std::char_traits<char> >:
와 같은 이상한 애들이 뜨면 프로젝트 우클릭 -> 정리를 한 번 해주고
다시 빌드를 해주시면 제대로 나옵니다.

1. 지금 스택에 들어간 eax(값은 1) 이건 main의 매개변수 인건가요? 

001426A5  mov         eax,dword ptr [hp]  
001426A8  push        eax  
001426A9  call        IncreaseHp (0141370h)  

main 쪽의 스택 프레임에 위치한 hp의 값을 복사해서 스택에 넣어주고 있는겁니다.
이건 어떻게 보면 main <-> IncreaseHp 중간 정도에 위치한 곳이라 딱히 누구의 것이라기 보다는
매개 변수를 넘겨주기 위한 중간 공간이라고 보시면 됩니다. (위 그림에서 [매개변수] 쪽)

2.

여기서 궁금한점이 그럼 main의 지역함수인 hp와 localValue는 언제 스택에 쌓이게 되는건가요?

main 함수도 다른 함수와 마찬가지로 프로그램이 시작할 때 '누군가'가 호출해준 상태이기 때문에
그 때 같이 자신의 스택 프레임을 갖게 되고, 이 때 hp와 localValue 영역도 같이 할당됩니다.
즉 main() 시작하자 마자 BP를 걸어보면 이미 hp, localValue 영역이 할당된 상태라고 보면 됩니다.
그리고 로컬 변수를 사용한다고 꼭 뭔가를 push해야 하는 것은 아니고,

esp 레지스터를 조작해서 영역을 잡아주는 (ex. esp에 40을 빼준다거나) 부분이 있는데
그 때 간접적으로 로컬 변수들 메모리가 할당된다고 보시면 됩니다.


어셈블리어로 CALL을 때리는 순간 돌아올 주소를 스택에 저장하고 IncreaseHp 함수 속으로 넘어가게 되는데
그렇다면  main의 지역변수는 스택에 쌓이지 못하게 되는 것 아닌가요?

main도 이미 누군가가 CALL main을 했다고 보시면 됩니다. (최초의 호출 함수)
따라서 당연히 main 의 지역변수도 스택에 위치한 상태입니다.

(마찬가지로 만약 func1()안에 func2()가 있다면 func1()의 지역변수 또한 쌓이지 못하고 CALL을 만나는 순간
func2()로 넘어가는 것 아닌가요?)
첨부한 스크린샷을 보면 main()에서 매개변수 -> 반환 주소 값 -> 지역변수를 스택에 쌓고
func1()으로 넘어가는 것처럼 묘사가 되어있는데 이게 어떻게 가능한건지 궁금합니다..

func1이 호출되고 있다면 이미 func1의 스택 프레임 (스택 메모리)도 할당받은 상태입니다.
이전에 누군가가 func1을 호출해줬을테니, 똑같이 자신의 지역변수를 사용하는 영역이 있겠죠.
참고로 매개변수를 넘길 때는 push 를 이용해서 직접 스택에 넣어주지만,
로컬 변수 영역은 esp 값을 조절하는 순간 간접적으로 같이 할당되는 것에 유의하세요.
esp의 값을 빼주는 행동은 마치 공허한 땅에 가서
내가 사용할 영역이라고 크게 선을 긋는 것과 유사합니다.

0

위 그림에서 지역변수(로컬 변수)가 매개변수 처럼 꼭  push을 이용해 직접적으로 할당 받아야 한다는 생각 때문에

이해를 못했던 것 같습니다. 자세한 답변 감사합니다!

움직이는YM님의 프로필 이미지
움직이는YM

작성한 질문수

질문하기