작성
·
136
0
Rookiss님 답변에서 궁금한 점이 생겼습니다.
은닉성 답변에서
void main()
{
Knight k;
int& hp = k.GetHp(); //< 일단 문제는 없음
}
이런 표현을 써주셨습니다.
제가 항상 헷갈리던게 반환형이 참조형이면 뭐가 반환되는가가 헷갈렸습니다.
예를 들어,
class Knight
{
public:
int _hp;
int& GetHp()
{
return _hp;
}
};
int main()
{
Knight k;
k._hp = 100;
int a = k.GetHp();
int& b = k.GetHp();
return0;
}
이런식으로 되어 있을 때
a와 b에서 일어나는 일의 차이를 잘 모르겠습니다
위 코드를 디스어셈블리로 살펴보면
int a = k.GetHp();
00A71939 lea ecx,[k]
00A7193C call Knight::GetHp (0A710B9h)
00A71941 mov eax,dword ptr [eax]
00A71943 mov dword ptr [a],eax
int& b = k.GetHp();
00A71946 lea ecx,[k]
00A71949 call Knight::GetHp (0A710B9h)
00A7194E mov dword ptr [b],eax
==> 이 실행문이 a에 하나 더 있는 걸 제외하고는 차이가 없습니다.
00A71941 mov eax,dword ptr [eax]
1. 참조값을 반환 한다는게 뭘 반환해 준다는 건가요?
- 예전 참조기초 강의에서
int* pointer = &number;
00BA2A59 lea eax,[number]
00BA2A5C mov dword ptr [pointer],eax
*pointer = 2;
00BA2A5F mov eax,dword ptr [pointer]
00BA2A62 mov dword ptr [eax],2
int& reference = number;
00BA2A68 lea eax,[number]
00BA2A6B mov dword ptr [reference],eax
이 어셈블리를 통해 int*로 선언된 변수나 int&로 선언된 변수 모두 number의 주소를 담는 바구니라는 것을 확인했습니다.
위와 같은 사실로 제가 생각해 봤을 때,
-> 제 생각 :
반환형이 int& 라면-> 참조값 &, 포인터처럼 주소를 담는 바구니는 돌려준다
-> 그 바구니를 따라가다보면 나오는 데이터는 int형이다.
int a = k.GetHp()의 뜻 -> 바구니를 따라가다보면 나오는 int형 데이터를 복사해서 a라는 바구니에 대입해주겠다.
int& b = k.GetHp()의 뜻 ->
00A7194E mov dword ptr [b],eax
여기서 eax가 return시 [_hp의 주소]를 가지고 있다는 것을 확인했습니다.
(008550C9 call Knight::GetHp (08510B9h) 를 F11로 들어가 확인해봤습니다)
즉 b는 _hp의 주소를 가지는 변수이다 -> 그런데 왜 메모리에서 &b를 찍으면 b의 주소에 _hp의 주소가 뜨는게 아니고 100이라는 값이 정확히 뜰까? -> C++에서는 참조라는게 그렇게 작동하니까
결국 리턴 값이 참조형으로 되어있으면 리턴되는 변수의 주소값이 넘어오는 것이다.
위와 같은 결론이 맞는 생각인지 궁금합니다.
답변 2
1
int a = k.GetHp()의 뜻 -> 바구니를 따라가다보면 나오는 int형 데이터를 복사해서 a라는 바구니에 대입해주겠다.
그게 맞습니다. 사실 눈으로 코드를 이해하려 하지 마시고
항상 메모리를 까보면 더 확실히 알 수 있습니다.
00A71941 mov eax,dword ptr [eax]
이 코드의 의미는 [eax 레지스터에 저장된 주소로 이동해서, 그 값을 다시 eax 레지스터에 저장]하는 것인데
원래 eax에는 _hp의 주소가 있고, 거길 이동해서 값을 꺼내서 eax에 저장하는 것입니다.
여기서 참조값은 해당 변수의 주소값을 이야기 하는것입니다.
이건 포인터나 참조나 별다른 차이가 없습니다.
두번째로 왜 &b를 하면 _hp의 주소가 뜨는게 아닐까?
~라는 질문을 하셨는데 매우 일리 있는 질문입니다.
C++ 관점에서 보면 참조란 결국 [원본을 건드리는] 형태로 동작하는 것입니다.
그리고 구체적으로 그것을 어셈블리로 변환했을 때 어떻게 만들지는 컴파일러 자유이고
C++ 관점을 훼손하지 않는 선에서 자유롭게,
최대한 빠르게 동작하는 코드를 만들 수 있습니다.
위의 경우에는 b라는 변수를 위한 공간을 따로 할당해서 주소를 넣기 보다는,
그냥 k._hp를 그대로 사용하는 쪽을 선택했고
그리하여 b는 사실상 C++에선 존재하지만,
어셈블리로 변환이 되면 '존재하지 않는' (k._hp랑 동일한) 아이로 인식되어
&b을 해도 우리가 예상한 주소 값이 들어있지 않은 것입니다.
참고로 이렇게 바로 사용하는 것으로 최적화가 가능한 이유는
참조 특성상 값이 한 번 정해지면 더 이상 수정이 불가능하기 때문입니다.
0