프로그램을 실행하면 해당 프로그램의 실행을 위한 메모리 공간이 운영체제에 의해서 미리 마련이 된다 .
그리고 이 메모리 공간 내에서 변수가 선언되고, 문자열이 선언된다.
메모리의 구성을 살펴보면 네가지 영역으로 구분된다.
코드 영역(Code Area)
코드 영역은 이름 그대로 실행할 프로그램의 코드가 저장되는 메모리 공간이다. 따라서 CPU는 코드영역에 저장된 명령문들을 하나씩 가져가서 실행을 한다.
데이터 영역(Data Area)
데이터 영역에는 전역변수가 static으로 선언되는 static변수가 할당된다. 즉 이영역에 할당되는 변수들은 프로그램의 시작과 동시에 메모리 공간에 할당되어 프로그램 종료 시 까지 남아있게 된다는 특징이 있다.
스택 영역(Stack Area)
스택 영역에는 지역변수와 매개변수가 할당된다. 이렇듯 이 영역에 할당되는 변수들은 선언된 함수를 빠져나가면 소멸된다는 특징이 있다.
힙 영역(Heap Area)
데이터 영역에 할당되는 변수와 스택 영역에 할당되는 변수들은 생성과 소멸의 시점이 이미 결정되어 있다. 그러나 프로그램을 구현하다 보면, 이 두 영역의 변수들과는 다른 성격의 변수가 필요하기도 하다. 그래서 C언어에서는 프로그래머가 원하는 시점에 변수를 할당하고 또 소멸하도록 지원을 하는데, 바로 이러한 유형의 변수들이 할당되는 영역이 힙 영역이다. 이 힙영역을 대상으로 하는 변수의 할당과 소멸에는 잠시 후 설명한다.
int num = 25;
int main()
{
int num1 =10;
fct(num1);
num1++;
fct(num1);
return 0;
}
void fct(int n)
{
int num2= 12'
...
}
만약 이런 코드가 할당되었다고 가정하고 프로그램 실행에 따른 메모리 상태 변화를 살펴보자.
코드영역은 변수가 할당되는 영역이 아니니 생략을 하겠다.
main함수가 호출되기 직전에 전역변수 num이 선언된다 .
이 num은 데이터 영역에 저장된다.
main 함수가 호출되기전 전역변수와 static 변수가 먼저 데이터 영여겡 할당이 되고 나서 main함수가 호출된다.
이어서 main함수를 호출하면 num1 지역변수가 스택영역에 할당이 된다.
다음은 fct 함수가 호출되는데 fct함수안에 매개변수 n과 num2가 스택영역에 할당이 된다.
fct가 반환을 함면서 fct 함수 호출시 할당되었던 매개변수와 지역변수가 소멸되면서 다시 스택에 할당되어 있던 n과 num2가 소멸하게된다.
++연산자로 인해 num=11로 변한다.
이어서 다시 fct함수 호출이 진행되고, 더불어 매개변수와 지역변수가 다시 스택에 할당이 된다.
마지막으로 다시 fct함수가 반환이 되어 스택영역에서 n과 num2가 소멸하게 된다.
main함수의 return 문이 실행되면 프로그램이 종료하게된다 .
종료되면, 운영체제에 의해서 할당된 메모리 공간 전체를 반환하게 되는데, 바로 그때가 전역변수가 소멸되는 시점이다.
즉 데이터 영역도 소멸이 된다.
지금까지 살펴본 내용을 기준으로 스택 영역의 특징을 찾아보면
함수 호출 순서가 다음과 같다고 가정했을 때.
main함수 -> fct1함수 ->fct2함수
이는 fct1함수 가 반환된 후에 fct2 함수 호출이되었다는 것이 아니고, fct1 함수 내에서 fct2 함수가 호출이 되었다는 뜻이다. 이러한 경우 지역(매개)변수의 소멸 순서는 다음과 같다.
fct2 ->fct1 -> main
이렇듯 먼저 호출된 함수의 스택공간일수록 늦게 해제된다는 것을 알수 있다. 그래서 메모리 영역의 이름이 슽잭이다.
스택(Stack)은 '쌓아 올려진 더미'를 뜻한다.
메모리의 동적할당
전역변수와 지역변수로 해결되지 않는 상황이 있다.
지역변수는 그 함수 내에서만 유효하기 때문에 함수를 나오게 되면 소멸이 된다.
하나의 전역변수로 해결할라고 하면 전역변수를 덮어 씌우는 경우도 있다.
즉
"함수가 매번 호출될 때마다 새롭게 할당되고, 또 함수를 빠져나가도 유지가 되는 유형의 변수"
다시 말해서, 지역변수와 같이 함수가 호출될 때마다 매번 할당이 이뤄지지만, 할당이 되면 전역변수와 마찬가지로 함수를 빠져나가도 소멸이 되지 않는 성격의 변수가 필요하다.
힙 영역의 메모리 공간 할당과 해제 : malloc 과 free
#include <stdlib.h>
void *malloc(size_t size); //힙 영역으로의 메모리 공간 할당
void free(void * ptr); //힙 영역에 할당된 메모리 공간 해제
->malloc함수는 성공시 할당된 메모리의 주소 값, 실패시 NULL반환
malloc함수는 포인터를 이용해서 메모리 공간에 접근하는 수 밖에 없다.
주소값을 반환하기 때문이다.
void *ptr = malloc(sizeof(int)); //int형 변수 크기의 메모리 공간 할당
*ptr = 20; //ptr이 void형 포인터이므로 컴파일 에러
즉 void형을 적절히 형 변환을 해서 할당된 메모리에 접근해야 한다.
int * ptr = (int *)malloc(sizeof(int));
malloc함수의 호출을 통한 메모리 공간의 할당을 가리켜 '동적할당(Dynamic allocation)' 이라 한다.
이유는 할당되는 메모리의 크기를 컴파일러가 결정하지 않고, 프로그램의 실행 준간에 호출되는 malloc함수가 결정하기 때문이다.
프로그램 종료시 할당된 모든 메모리 공간은 운영체제에 의해서 전부 해제가 된다.
하지만 큰 작업을 할시 메모리가 부족할 수 있으므로 반드시 free선언을 해 사용이 끝난 변수를 할당을 해제해야한다.
malloc함수와 free함수를 이용하면 메모리 공간의 할당과 소멸의 시점을 프로그래머가 직접 결정할 수 있기 때문에, 전역변수나 지역변수가 감당하지 못하는 일들을 감당할 수 있다.
#include <stdlib.h>
void *calloc(size_t elt_count , sie_t elt_size);
->성공 시 할당된 메모리의 주소 값, 실패시 NULL반환
위의 함수 원형에서는 malloc함수와 달리 calloc 함수는 두 개의 숫자를 인자로 전달받는다.
malloc의 호출방식은 120 바이트를 힙 영역에 할당해주세요 라면 ,
calloc의 호출 방식은 4바이트 크기의 블록(elt_size) 30개를(elt_count) 힙 영역에 할당해 주세요 이다.
즉 인자를 전달하는 방식에서 차이를 보낸다. 결과적으로는 120바이트가 동일하게 할당된다.
또 다른 점은 malloc함수는 별도의 값으로 초기화 하지 않아 쓰레기 값으로 채워지지만 , calloc함수는 할당된 메모리 공간의 모든 비트를 0으로 초기화 시킨다.
메모리 할당 해제를 할 경우 똑같이 free선언을 하면 된다.
한번 할당된 메모리 공간은 그 크기를 조절할 수없지만, 힙 영역에 있는 메모리 공간은 확장이 가능하다.
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
->성공 시 새로 할당된 메모리의 주소 값, 실패 시 NULL반환
이 함수의 첫 번째 전달인자로, 확장하고자 하는 힙 메모리의 시작 주소 값을 전달하고, 두 번째 전달인자로는 확장하고자 하는 메모리의 전체 크기를 전달한다. 즉 매개변수 ptr과 size를 이용해 메모리의 크기를 조절 할 수 있다.
malloc 함수가 반환한 주소 값과 realloc 함수가 반환한 주소 값이 같은 경우도 있고 , 다른 경우도 있는데,
이는 malloc 함수의 메모리 공간을 이어서 뒷공간이 넉넉하면 값이 같고, 부족하면 다른 힙영역에 할당될 수 도 있다.