메모리 영역
-
코드 영역
코드 영역은 이름 처럼 작성한 코드를 저장하는 영역입니다. -
데이터 영역
데이터 영역은 전역 변수와 정적 변수를 저장하는 영역입니다.
전역 변수는 프로그램이 시작되면서 초기화됩니다.
그러므로 데이터 영역은 프로그램 시작시에 이미 메모리가 확보되어 크기가 변하지 않음을 알 수 있습니다. -
힙 영역 힙영역은 동적 메모리를 할당하는 영역입니다.
C언어로 코드를 작성할 때 직접 메모리를 할당할 때가 있습니다.
이 때 메모리를 할당하는 영역이 바로 힙 영역입니다.
힙에 저장된 메모리는 함수호출이 종료되어도 사라지지 않기 때문에, 개발자가 free()함수를 통해 명시적으로 해제하거나 프로그램이 종료되어야 사라진다.
프로그램은 메모리 주소에 따라 힙 데이터에 접근한다.
힙 공간은 스택영역과 달리 크기에 제약이 없기때문에 메모리만 충분하다면 필요한만큼 확장할 수 있다. -
스택 영역
스택 영역은 지역 변수와 매개변수를 저장하는 영역입니다.
지역 변수와 매개 변수 특성 상 해당 함수의 호출과 실행이 완료되면 사라집니다.
그러므로 데이터가 저장되었다가 지워지기를 반복하는 영역임을 알 수 있습니다.
(자료구조에서의 스택을 알고 있으면 이해하기 편할 것이다.)
메모리할당 함수 (malloc,calloc,realloc)
메모리 관련함수는 헤더파일
<stdlib.h>
또는<malloc.h>
에 있다.
실제값 : *(p + i) == p[i];
주소 : p + i == &p[i];
malloc
: 동적 메모리 함수
- 메모리를 미리 일정량 만큼 잡아두지 않고, 필요할때마다 동적으로 할당한다.
- 필요할 때마다 동적으로 할당해서 메모리낭비를 줄여준다.
- 형식 : 포인터변수 = (포인터변수의 데이터형 *)malloc(포인터변수의 데이터형 크기 * 필요한크기);
#include<stdlib.h>
int main(){
int *p;
p = (int *)malloc(sizeof(int)*10); // int형은 4바이트니깐, 총 40바이트를 할당한 상태이다.
free(p);
}
- 크기는 바이트형이고, malloc을 이용해 필요한만큼 동적할당을 해준다.
- 사용후 free() 함수를 통해 할당된 공간을 반납해준다.
calloc
함수
- 요청한 메모리공간을 0으로 초기화하여 할당함.
- 형식 : 포인터변수 = (포인터변수의 데이터형 *)calloc(포인터변수의 데이터형 크기 * 필요한크기);
- malloc함수로 공간을 할당하면, 가비지값(쓰레기값)들이 들어가는데, calloc()함수를 이용해 할당하면,
원하는만큼 공간을 할당후 메모리공간도 0으로 초기화 해준다.
realloc
함수
- 새로 동적메모리를 할당받고, 이전에 할당받은 메모리값을 복사하는 함수
- 단독으로 사용이 불가능하며 이전에 malloc또는 calloc으로 할당받았던 경험이 있어야함.
- 형식 : 포인터변수 = (포인터변수 *)realloc(기본포인터, 포인터변수의 데이터형 크기 * 필요한크기);
#include<stdlib.h>
int main(){
int *p;
p = (int *)malloc(sizeof(int));
p = (int *)realloc(p, sizeof(int)) // 이전라인에서의 p의값을 복사해서 넣는다.
free(p);
}
메모리문제
메모리 릭(Memory Leak)
메모리 릭은 프로그램이 불필요한 메모리를 계속 점유하는 현상이다.
malloc() 또는 calloc() 등으로 동적메모리를 힙에 할당해놓고, free()를 통해 명시적으로 메모리를 해제 하지 않으면 프로그램이 작동하는동안 지속적으로 불필요한 메모리를 점유하게 된다.
나중에 언급하겠지만, 이러한 이유로 동적할당과 할당해제는 같은 코드블럭내에 위치하는 것이 유리하다.
댕글링 포인터(Dangling Pointer)
댕글링 포인터(또는 허상 포인터)는 이미 해제된 메모리를 가르키는 포인터를 뜻한다.
동적할당된 곳을 포인터가 가리키고 있는 상태에서 해당 동적할당을 해제하게 되면, 해당 공간을 가리키고 있던 포인터에 있는 주소 공간은 아무런 의미가 없게된다.
이 때 포인터에 저장된 주소공간에서 데이터를 가져오거나 어떤 동작을 수행하면 문제가 될 수 있다.
차라리 아무것도 가져오지 않으면 괜찮은데, 가비지값을 가져오면서 정상동작 하는것 처럼 보이다가 예상치 못한 오류가 뜰 수도 있다.
버퍼 오버플로우 (Buffer Overflow)
버퍼 오버플로우는 배열 혹은 동적으로 할당된 메모리 영역을 벗어나서 쓰기 작업을 수행하는 것이다.
널포인터 익셉션(Null Pointer Exception)
Null로 초기화한 메모리 영역에 접근하면 발생하는 에러이다.
메모리 릭을 줄이는 방법
malloc과 free를 한 블록에서 하기
메모리 할당과 해제는 한블록 내에서 한번씩만 한다.
#include <stdlib.h>
struct Example {
int a;
int b;
char c;
};
void init_example() {
struct Example *example = malloc(sizeof(*example)); // 메모리 할당
if (example == NULL) return;
example->a = 1;
example->b = 2;
example->c = 'c';
free(example); // 해제
}
int main() {
init_example();
}
출처 : 커널패닉
위코드에서 init_example 코드블럭 내에 malloc 동적할당과 free 메모리해제를 같은 블럭내에서 한번씩 해줬기 때문에 위코드에서는 메모리릭이 발생하지 않는다.