[알고리즘] 정적할당, 동적할당, 메모리

정적할당, 동적할당, 메모리

정적할당

정적할당은 일반적으로 우리가 하는 변수 선언이다.

변수나 함수, 배열 등을 선언함으로써 메모리에 할당할 때, 메모리에게 미리 그 크기를 알려주어 메모리 공간의 필요량을 알려주는 방법이다.

int main(){
  int num1; // int자료형이니 4바이트만큼의 메모리 할당
  double num2; // double자료형이니 8바이트만큼의 메모리 할당
  int array[10]; // 4*10바이트 메모리 할당
}

동적할당

동적 메모리 할당과 정적 메모리 할당의 차이점은 메모리의 크기를 프로그램을 실행하기 전에 결정하느냐, 프로그램을 실행하는 도중에 결정하느냐가 된다. 동적 메모리 할당은 프로그램이 실행 도중에 동적으로 메모리를 할당받는 것을 말한다.
동적할당에 대한 본격적인 설명에 앞서 메모리에 관하여 짚고 넘어가면 이해가 빠르다.

메모리

메모리 계층 구조에는 크게 네 가지 종류가 있고 다음과 같은 특성을 가지고 있다.

여기서 잠깐, 메모리는 왜 굳이 네 개 씩이나 분할되어 필요한걸까?

이론상으로는 하드디스크 단 하나와 CPU(캐시와 레지스터가 없는) 만으로도 동작에는 문제가 없다. 하드디스크는 대체로 용량이 아주 크기 때문에 하드디스크에 데이터들을 담아도 큰 문제는 없다. 다만 하드 디스크는 정말! 느리다. 따라서 속도 및 성능의 문제 때문에 하드디스크보다 더 빠른 메모리인 RAM을 CPU와 하드디스크 사이에 추가한 것이다.

(물론 CPU에는 해당 요소들 뿐만 아니라 컨트롤유닛 버스인터페이스 와 같은 다른 요소들도 포함되어 있지만 주제상 표기하지는 않았습니다.)

여기서 ALU는 Arithmetic(산술,연산) Logic Unit의 약자로 실제로 CPU 안에서 연산을 담당하는 장치이다.

CPU 안에 메모리가 필요한 이유는 ALU의 빠른 연산을 돕기 위해서이다. RAM에서 필요한 데이터들을 레지스터 안으로 가져와 저장하고, ALU는 레지스터에 저장된 데이터를 가져가 연산을 한다. RAM에서 그때 그때 데이터를 ALU로 바로 가져오는 것 보다 CPU내부의 메모리를 사용하는 것이 더 빠르다. (고속도로와 국도의 차이 같은 느낌이라고 하네요..?)


여기까지 컴퓨터 내부의 메모리에 대하여 간단하게 살펴보았다. 그렇다면 우리가 C/C++ 코딩을 할 때 쓰이는 포인터를 다룰 때 주로 말하는 ‘메모리의 주소값’에서의 메모리는 어떤 메모리를 말하는 것일까?

바로 가상메모리 이다.

가상메모리

C/C++ 기준의! 가상메모리에 대한 설명입니다.

가상메모리란 RAM을 관리하는 방법의 하나로(RAM은 현재 실행중인 프로그램을 관리하니, 그 프로그램의 가상메모리를 할당해주는거니 RAM의 관리 방법 중 하나가 맞는것으로..추정된다…확인해볼것…), 실제 메모리(RAM, 레지스터,캐시, 하드디스크)를 하나의 구조로 보이게끔 해주는 가상의 메모리이다. 각 프로그램에 실제 메모리 주소(물리 주소)가 아닌 가상의 메모리 주소(가상주소 혹은 논리 주소)를 준다. 가상 주소의 범위를 가상 주소 공간이라고 하며, 가상 주소 공간은 메모리 관리 장치에 의해 물리주소로 변환된다.

프로그램을 실행하며 가상의 메모리가 필요한 이유는 다음과 같다.

가상메모리를 효과적으로 사용하기 위해 가상메모리는 역할에 따라 크게 네 가지로 분류된다.

컴파일타임 및 런타임

위에서 가상메모리를 설명하며 컴파일타임과 런타임에 대한 이야기를 하였다. 각각에 대하여 설명하고자 한다.

C언어는 처음 만들어질 때 컴파일러 라는 프로그램과 함께 출시가 되었다. 이 컴파일러는 고수준 언어인 C언어로 구성된 프로그램 코드를 어셈블리 코드로 변환시켜주는 역할을 한다. 어셈블리어는 기계어 (CPU가 읽어서 실행할 수 있는 0과 1로 이루어진 명령어의 조합) 와 일대일 대응이 되는 언어로써, 기계어의 각 명령어에 대해 사람이 알아보기 쉬운 기호로 작성된 것이다.
어셈블리어는 바이너리 형태의 명령어를 사람이 알아볼 수 있게 재편성한 언어임으로 결국 컴퓨터가 읽을 수 없다. 따라서 이러한 어셈블리어는 어셈블러를 통해 다시 기계어로 변환이 되어 컴퓨터가 읽을 수 있게 해야 하고, 이와 같이 컴파일러와 어셈블러의 작업이 완료되면 .obj 오브젝트 파일이 생성된다. 그리고 이 오브젝트 파일(CPU에게 일을 시키기 위한 바이너리 형태(기계어)의 명령어) 및 기타 라이브러리 파일을 하나로 묶어 OS에서 요구하는 포맷에 맞게 다시 구성한 실행파일을 만든다.

설명이 다소 복잡한데..정리하자면 다음과 같다.

…그래서 동적할당은

동적할당은 프로그램 실행 도중 가상메모리의 힙영역에서 필요한 크기만큼의 메모리를 할당받고, 다 쓴 메모리는 꼭 반납해준다.(반납 안하면 메모리 누수가 발생합니다!)

앞서 작성하였지만 동적 메모리 할당과 정적 메모리 할당의 첫번째 차이점은
할당하는 메모리의 크기를 프로그램을 실행하기 전에 결정하느냐, 프로그램을 실행 도중에 결정하느냐가 된다.

변수를 정의하는 경우를 다시 예로 들어보자. 동적할당은 메모리의 크기를 지정해줄 때 변수를 사용할 수 있다. (정적할당은 안됨)

int main(){
  // 정적할당
  int length1;
  cin >> length1;
  int arr1[length1]; // 컴파일에러!!! 안됩니다!!

  // 동적할당
  int length2;
  cin >> length2;
  int* arr2 = new int[length2]; // 내가 원하는만큼 메모리 할당이 가능해졌다>_<!! 꺄힝!!
}

정적할당의 케이스가 에러가 나는 것은 위의 메모리 이론을 보고 나면 당연한 이치이다.
위의 코드에서 int함수 안의 지역 변수들은 런타임 이전인 컴파일 타임에 스택 영역에 메모리가 할당된다. 하지만 배열의 크기를 맡는 변수 length1의 값은 프로그램이 실행(런타임) 되는 도중에 입력되는 값이다. 따라서 지역변수인 int형 배열 arr1은 크기 값을 실행 되기 전까진 알 수 없으니, 컴파일 타임에 스택 영역에 메모리가 할당될 수 없기 때문에 컴파일 오류가 나는 것이다.

동적할당의 케이스는, 앞서의 메모리 이론처럼 프로그램이 실행되는 도중 힙 영역에 메모리를 할당받는다. 배열의 크기로 쓰이는 변수 length2의 값은 프로그램이 실행된 뒤 사용자에 의해 입력받는 값이다. 그래서 실제로 length의 값이 얼마가 될지는 프로그램을 실행하고 나서야 알 수 있다. 이렇게 메모리의 크기가 프로그램이 실행되는 도중에 동적으로 결정될 수 있다.

동적 메모리 할당과 정적 메모리 할당의 또 하나의 차이점은 바로 메모리를 할당하고 해제하는 시점이 자유롭다는 점이다.

다시 정적 메모리 할당을 살펴보자. 함수 안에서 정의한 변수는 함수의 실행이 끝나면서 소멸한다. 그것이 main()함수이건 우리가 만들어준 임의의 함수이건 상관 없다. 이는 함수가 실행됨으로써 함수 안의 매개 변수들에 대한 메모리가 스택 영역에 할당되고, 함수가 종료되는 시점에는 스택 영역에 저장된 해당 메모리들이 해제되고 변수도 소멸된다는 말이다.

하지만 동적 메모리 할당으로 할당된 메모리는 사용자가 직접 해제하기 전 까지는 절대 컴퓨터에 의해서 해제되는 일이 없다. 메모리를 할당하는 시점도 종료하는 시점도 개발자 맘대로 정할 수 있다는 점이 정적할당과는 다른 또 다른 특징이다.


C언어의 경우 malloc함수나 calloc함수를 사용하여 동적할당을 하는데, C++의 경우 new 연산자를 통하여 동적할당을 할 수 있다. 문법은 다음과 같다.

변수의 동적 메모리 할당은 물론 객체의 동적 메모리 할당 역시 가능하다. 특히 자료구조 구현 시 아주 유용하게 쓰인다.

// Node라는 객체를 동적할당
Node* startNode = new Node(value,NULL); //클래스명Node 객체명startNode
Node* startNode = new Node(); //생성자에 매개변수 없을 시

동적 할당을 해제하는 문법은 다음과 같다.

new, delete는 변수 및 객체 하나를 동적으로 할당/해제 하는 것이고, new[], delete[]는 배열 하나를 동적으로 할당 및 해제하는 것이다. 각각의 상황에 맞게 써주어야 한다. 당연한 이야기지만 해제는 한 번만 할 수 있다.

delete[] arr; // 동적 할당으로 선언한 배열의 경우
delete startNode; 


참고URL

http://blog.daum.net/blog/BlogTypeView.do?blogid=0Nu8o&articleno=40&categoryId=1&regdt=20090518160416
http://blog.naver.com/skout123/50125764985
http://blog.naver.com/skout123/50115353896
http://blog.naver.com/skout123/50127618681
http://blog.naver.com/skout123/50127687176
http://blog.naver.com/skout123/50128375008
http://dsnight.tistory.com/50
http://multithread.tistory.com/entry/C-%EA%B0%9D%EC%B2%B4%EC%83%9D%EC%84%B1-%EB%91%90%EA%B0%80%EC%A7%80-%EB%B0%A9%EB%B2%95%EA%B3%BC-%EC%9D%98%EB%AC%B8%EC%A0%90new%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%8F%99%EC%A0%81%ED%95%A0%EB%8B%B9%EA%B0%9D%EC%B2%B4
https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EB%A6%AC
%EB%A7%B5
https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EB%A6%AC_%EA%B3%84%EC%B8%B5_%EA%B5%AC%EC%A1%B0
http://rhkd2060.blog.me/140184910779