해당 글은 fundamental C++을 참고하여 작성되었습니다.
이번 글에서는 클래스의 메모리가 어떻게 배치되는지와 메모리의 오프셋에 대해 알아보겠습니다.
간단한 코드를 하나 작성하겠습니다.
myClass타입의 객체 temp를 생성했습니다.
선언된 temp의 메모리 구조는 대략 이렇습니다. 중요한 것은 정적 멤버 변수인 m_SInt와
멤버 함수인 m_func는 메모리 영역에 포함되어 있지 않다는 점입니다. 실제로 메모리 영역에 포함되어 있는 것은 일반 멤버 변수들뿐이죠.
멤버 변수는 객체의 상태나 정보를 나타내기 위해서 존재합니다. 그에 비해서 정적 멤버 변수는 객체가 아닌 클래스의 상태와 정보를 나타냅니다. 따라서 멤버 변수는 각 객체마다 존재하지만, 정적 멤버 변수는 하나만 존재할 수 있습니다. 이는 전역변수와 같이 어떤 객체에서든 정적 멤버 변수에 접근할 수 있기에, m_SInt는 전역변수가 저장되는 전역 메모리 데이터 영역에 4바이트만큼 존재하며, myClass에 모든 객체가 이를 공유할 수 있습니다.
그렇다면 실제 메모리 영역에서 멤버 변수들은 어떻게 저장될까요?
위에 코드는 0을 myClass* 타입으로 캐스팅하여 시작 주소를 0으로 맞춰놓은 뒤, 각 멤버 변수의 시작 위치를 출력시키는 코드입니다. 참고로 멤버 변수의 주소는 클래스에서 선언한 순서대로 쌓이게 됩니다. 한번 코드를 실행시켜봅시다.
뭔가 이상하죠? byte의 값으로 봤을 때 이상적인 출력결과는 분명 [1,4,5]인데 말이죠. 여기에는 한가지 비밀이 숨어있습니다. 바로 오프셋이라는 친구가말이에요.
오프셋의 정의부터 먼저 짚고 넘어갑시다.
간단하게 풀어설명하자면, "두개의 값 사이의 간격"정도로 생각하면 되겠습니다.
컴파일러는 생각보다 하는일이 꽤 많습니다. 소스코드를 읽어 어셈블리언어로 변환하는게 주된 일이지만, 코드의 성능을 높이기 위해 코드를 직접 최적화하기도 합니다.(예를 들면 암시적 형변환이 있겠네요.) 클래스 객체의 메모리 배치도 컴파일러가 하는 일 중에 하나입니다.
컴파일러는 /ZpN 옵션을 가지고 객체의 메모리 오프셋을 설정합니다. 비주얼 스튜디오에서는 프로젝트속성에서 확인 할 수 있습니다.
컴파일러는 멤버 변수를 메모리에 위치시킬 때, 변수 타입의 크기와 N(기본값은 8입니다)중 작은 쪽의 최소 배수가 되도록 조정을 하게 됩니다. 그렇기에 m_char는 첫 멤버 변수이므로 0에 위치하게 되며, m_int는 (4,8)중에서 작은 쪽인 4의 배수인 4의 오프셋을 가지게됩니다. 이후 double는 1 + 3 + 4 = 8이므로 자연스럽게 오프셋 8에 위치합니다.
감이 잘 안잡히죠? 예시로 몇개 더 해봅시다.
1.[0, 1, 9]
2.[0, 8, 9]
3.[0, 8, 16]
1.[0, 4, 8]
2.[0, 8, 12]
3.[0, 4, 12]
1.[0, 8, 12]
2.[0, 8, 9]
3.[0, 8, 16]
기본적인 메모리 배치만 알면 되겠고, Zp1, Zp2 등등... 많긴 한데 그것들까지 다 할 필요성까지는 느끼지 못했네요. 오늘은 여기까지 하고 정답은 댓글로 남겨놓겠습니다.
그럼 바윙~
순서대로 3, 1, 1이 정답입니다