킹의 개발일지

C언어의 메모리 구조, 메모리 동적 할당 본문

프로그래밍 언어/C

C언어의 메모리 구조, 메모리 동적 할당

k1ng 2022. 12. 5. 10:47

 

우리가 만든 .c 소스코드들의 변수들은 어디에 저장되는 것일까? 그리고 이런 변수들은 언제 불러와지고 언제 소멸될까?

이번에는 이런 궁금증들을 풀어보고자 c언어의 메모리 구조에 대해서 설명하려 한다.


먼저, 프로그램이 실행되기 위해서는 프로그램이 '메모리'라는 저장공간에 로드돼야한다. 이 메모리라는 공간에 우리가 선언했던 변수가 저장되고, 문자열들이 선언되는것이다. 그리고 이러한 저장공간은 운영체제가 만들어준다.

 

메모리 구성

프로그램 실행시 운영체제에 의해서 만들어지는 메모리의 구조는 다음과 같이 네 영역으로 구분할 수 있다.

tcp school에서 가져온 이미지

이렇게 나눠진 영역별로 저장되는 데이터 유형들이 다 다른데, 한번 살펴보자.

 

코드 영역

코드영역은 실행할 프로그램의 코드가 저장되는 메모리 공간이다. 따라서 CPU는 코드영역에 저장된 명령문들을 하나씩 가져가서 실행한다.

 

데이터 영역

데이터 영역은 전역변수와 static으로 선언되는 static변수가 할당된다. 이 영역에 저장되는 변수들은 프로그램이 시작되면 메모리 공간에 할당되어 프로그램 종료까지 남아있게 된다는 특징이 있다.

static 변수의 경우, 함수호출이 끝났음에도 불구하고 데이터 영역에 계속 남아있게 된다. 또한 static 변수가 함수안에서 선언 되더라도 초기화는 딱 한번 일어나게 된다는 특징이있다. 

 

스택 영역

우리가 평소 선언하는 지역변수들이나 매개변수들이 이 공간에 할당된다. 그리고 이 영역에 할당되는 변수들은 선언된 함수를 벗어나면 소멸된다는 특징이있다. 이는 static 변수와 큰 차이를 보인다.

 

힙 영역

데이터 영역에 할당되는 변수와 스택영역에 할당되는 변수들은 생성과 소멸의 시점이 이미 결정 되었있다. 데이터 영역은 프로그램의 시작과 끝에, 스택영역은 블럭을 나가거나 함수호출이 끝났을 때 소멸된다. 그래서 컴파일 타임에 메모리 크기가 결정된다. 하지만  c언어는 원하는 시점에 변수를 할당하고 또 소멸하도록 지원해주는데, 이러한 유형의 변수들이 저장되는 공간이 바로 힙 영역이다.

 

이 힙 영역은 사용자에 의해 메모리 공간이 동적으로 할당되고 해제되는데, c에서는 대표적으로 mallocfree함수가 있다. 그리고 이 영역은 런타임 동안 크기가 결정되는데, 스택과 같은 영역과 대조되는 개념이다.

 


메모리 동적 할당

위에서 메모리 영역을 설명하면서 힙 영역에 대해서 말한 바 있다. 이 영역은 사용자가 직접 할당해주고 할당 해제 해주는데, 따라서 원치 않는 시점에서 값을 저장한 변수가 사라지거나 하는 문제를 해결 할 수 있다.

 

우리는 함수가 매번 호출될 때마다 새롭게 할당되고 또 함수를 빠져나가도 유지되는 유형의 변수가 필요할 때 가 있다.

이를 위해 c언어에서는 위에서 잠깐 이야기 했던 mallocfree라는 함수를 통해서 힙 영역에 할당하고 소멸할 수 있다.

 

아래 코드는 malloc과 free의 프로토타입을 보여준다.

#include <stdlib.h>
void *malloc(size_t size);
void free(void *ptr);

 

malloc 함수는 매개변수 size만큼 힙에 동적으로 영역을 할당하고 해당 영역의 주소를 나타내는 void * 를 반환한다.

참고로 void 포인터는 아무 가공도 되어있지 않기에, 이 포인터를 이용해서는 할당된 메모리에 접근할 수 없다. 때문에 캐스팅을 해주어서 사용할 수 있다.

또한 size만큼 사용 가능한 메모리가 없을 경우에는 NULL을 반환한다.

int *ptr = (int *)malloc(4 * sizeof(int));

 

그리고  free함수는 힙 영역에 할당된 메모리 공간을 해제 해준다.

free(ptr);

 

힙 영역은 프로그래머가 관리하는 메모리 공간이라고 할 수 있으며 이 공간은 프로그램이 종료될 때까지 존재하게 된다. 그래서 메모리를 할당만 하고 해제를 해주지 않으면 메모리 사용량은 계속해서 증가하게 될것이다. 따라서 malloc으로 할당된 공간은 free 함수로 꼭 회수해주어야 하는데, 그렇지 않으면 메모리 누수가 발생할 수 있다. 그래서 malloc 함수와 free함수는 주로 쌍으로 이루어 호출된다.

 

malloc과 free를 사용한 기본적인 예시를 보자

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int *ptr1 = (int *)malloc(sizeof(int));
    int *ptr2 = (int *)malloc(7 * sizeof(int));    
    int i;
    
    *ptr1 = 20;
    for(i = 0; i < 7; i++)
        ptr2[i] = i + 1;
    
    printf("%d \n", *ptr1);
    for(i = 0; i < 7; i++)
        printf("%d \n", ptr[i]);
    
    free(ptr1);
    free(ptr2);
    
    return 0;
}

ptr2는 4바이트 공간을 총 7개 할당 받았는데, 이 공간은 배열처럼 접근이 가능하다. 따라서 for 문을 통해 ptr[i] 처럼 접근하는 모습을 볼 수 있다. 그리고 할당 받은 공간은 꼭 free를 통해서 회수 해야한다.

참고로 malloc으로 반환된 주소를 int 포인터로 캐스팅을 할 경우, 주소에 증가 연산자를 붙혀주면 int 사이즈 만큼 다음 주소로 이동한다.

 

free 함수를 호출하지 않아도, 물론 프로그램이 종료되면 운영체제에 의해서 전부 회수되긴 한다.

하지만 현실에선 프로그램을 실행하고 종료하는것이 그렇게 간단하지 않을것이며, 덩치가 큰 프로그램의 경우 컴파일 하는데만 한 나절 걸릴수 도 있다. 따라서 malloc을 호출하면 의식적으로라도 free를 꼭 호출해주는것이 좋다!

'프로그래밍 언어 > C' 카테고리의 다른 글

C 프로그래밍  (0) 2022.11.28