신입 게임 개발자의 프로그래밍 일기

[C++ 입문자에서 벗어나기]Chapter_2: 선언과 정의#1 본문

Language/C++

[C++ 입문자에서 벗어나기]Chapter_2: 선언과 정의#1

KFGD 2017. 11. 5. 09:30

[C++ 입문자에서 벗어나기]Chapter_2: 선언과 정의#1


이전 포스팅까지는 Chapter_1: 타입(Type)에 대해 이야기했었는데 이번 포스팅부터는 선언과 정의에 대해 이야기해보려고합니다.

개인적으로는 선언과 정의에 대해 무시하면서 IDE(통합개발환경, Integrated Development Environment)의 편리함에

기대어 코딩을 해왔었는데 "Fundamental C++ 프로그래밍 원리"선언과 정의 부를 읽으며 여러 생각을 하게 되었습니다.


[선언과 정의]


저는 일반적으로 선언과 정의에 대해 이야기해보라 하면 아래와 같이 이야기했을 겁니다.


선언: 어떤 객체가 있음을 알림

정의: 어떤 객체를 위한 메모리 영역을 확보


정의는 객체를 이용하기 위해서 필수적인 절차이지만

선언이 왜 따로 구분이 있는지에 대해서는 이야기하기가 힘들었습니다.


int a = 0;    //변수 a를 정의

std::cout << "a : " << a << std::endl;    //변수 a를 출력


보통 선언만 하는 경우는 없었습니다. 

대체적으로 선언과 동시에 정의가 수반되었으니까 선언에 대해 깊게 생각해보지 않았다는 게 옳을 것입니다.

선언과 정의에 대해서 이야기하려면 아직 포스팅되지 않은 [C++ 입문자에서 벗아나기]Chapter_3: 빌딩 와 Chatper_4: 가상메모리 부분의 

일부분에 대해서 이야기해야 하는게 이해하기 편하다고 판단하여 설명을 하면서 그것들을 언급하겠습니다.

우선 간단하게 C++의 가상메모리에 대해 이야기해보겠습니다!


[C++의 가상메모리]


가상메모리는 프로그래머들이 프로그래밍할 시 사용하는 가상의 메모리라고 이야기 할 수 있습니다.

(컴퓨터를 조립할 때 구성되는 물리적인 메모리 DRAM을 뜻하는게 아닙니다.

자세한 이야기는 Chapter_4: 가상메모리에서 다루겠습니다.)


C++의 가상메모리는 아래와 같이 크게 5개의 부분으로 나눌 수 있습니다.


- Code 영역: 함수 정의 및 저장 공간

- Data 영역: 초기화된 전역 및 정적 변수들의 저장 공간

- BSS 영역: 초기화 되지 않은 전역 및 정적 변수들의 저장 공간

- Heap 영역: malloc 및 new로 생성된 객체들의 저장 공간

- Stack 영역: 함수 호출 시 전달되는 인자나 함수 안에 정의되는 지역변수가 저장되는 공간


왜 이 가상메모리에 대해 이야기했는지는 변수들마다 차지하는 메모리 영역이 다르다는 것을 이야기하고 싶어서입니다.

정의되는 위치에 따라 변수들은 자동 초기화 유무가 달라지고 초기화 시점 및 차지하는 메모리의 저장 위치마저 달라집니다.

 

변수 종류

자동 초기화 유무

초기화 시점

메모리 저장 위치

전역 변수

O

프로세스 시작 시

Data

정적 변수

O

프로세스 시작 시

Data

멤버 변수

O

생성자 호출 시

Stack or Heap

지역 변수

X

함수 호출 시

Stack


전역변수와 정적변수는 모든 것이 동일하지만 위의 표에서는 표시되지 않은 접근 범위라는 부분에서 차이가 있습니다.
전역변수와 달리 정적 변수는 접근 범위가 존재합니다.
전역 변수와 정적 변수는 프로세스 시작 시에 초기화가 진행되면 Data영역을 차지합니다.

지역 변수는 함수 호출 시에 Code 영역에 저장되는 함수 정의를 활용하여 초기화됩니다.

멤버변수에 대해서는 보다 자세하게 이야기해보겠습니다.


[클래스 정의 그리고 멤버 변수]


우리는 프로그래밍을 할 때 클래스의 멤버 변수를 주로 이름으로 접근하여 사용합니다.

여기서 착각 할 수 있는 것이 있는데 소스코드가 어셈블리로 변환되면 결국 저 이름은 메모리 오프셋으로 바뀌게 됩니다.

결국 우리는 멤버변수의 이름을 사용하지만 클래스의 멤버 변수에 접근하는 방식은 클래스 정의에 따라 

해당 멤버 변수의 메모리 오프셋을 계산하여 접근하는 방식입니다. 


절대로 멤버변수의 이름마다 고정된 메모리 영역이 있는 것이 아닙니다.


그럼 클래스 정의는 어느 메모리에 있는 걸까요?

다른 변수 또는 함수의 정의처럼 실행 프로그램의 가상 메모리에 존재하는 걸까요?

아닙니다!


컴파일러가 소스 파일을 어셈블리를 변환하는 중에만 클래스 정의를 컴파일러가 사용하는 메모리에 적재합니다.


컴파일러는 컴파일 중 소스 파일 속 멤버 변수들을 클래스 정의에 따라 "정의된 객체의 메모리의 주소 + 멤버 변수의 오프셋"으로 치환합니다.


IDE를 사용하면 간과하기 쉬운 한가지가 더 있습니다.

컴파일러는 오직 소스파일(.cpp)단위로만 독립적으로 컴파일을 수행하므로 


다른 소스파일에 동일한 클래스 정의가 있어도 상관이 없습니다.

단! 같은 이름의 클래스인데 정의된 코드가 다르다면 중복 정의 에러가 발생하게 됩니다.


이번에는 과연 정말로 멤버변수가 이름이 아닌 오프셋으로도 접근이 되는 지를 직접 확인해봅시다.



  


 



Alphabet 클래스 정의를 정의하고 cout 함수에서 첫번째는 이름으로 멤버변수 값을 출력하고 

두번째에는 오프셋으로 멤버변수 값을 출력해보았습니다.

두가지 방식 모두 동일한 값을 출력함을 확인할 수 있습니다.

Comments