작성
·
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
우선 어셈블리 코드에서 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을 이용해 직접적으로 할당 받아야 한다는 생각 때문에
이해를 못했던 것 같습니다. 자세한 답변 감사합니다!