Search
Duplicate

malloc, free 그리고 dangling pointer가 생기는 이유

간단소개
malloc, free의 간단한 과정과 dangling pointer가 생기는 이유
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
C
Scrap
태그
c언어
9 more properties
c 프로그래밍을 좀 해봤다 하는 사람들은 malloc이 메모리 공간을 할당해주는 함수이고, free는 메모리 공간을 해제하는 함수라는 것을 알고 있을 것이다.
여기서 좀 더 나아가서 이로 인해 생기는 문제점인 dangling pointer를 알고 있는 사람도 있을 것이다.
하지만 문득 궁금할 것이다.
왜 free 함수를 써서 메모리 공간을 해제했는데 포인터 변수는 아직도 메모리 공간을 가리키고 있지?
이는 malloc과 free가 단순히 메모리 공간을 생성하고 없애는 개념이 아니기 때문이다.

malloc과 free의 매커니즘

프로그램에 메모리 공간을 할당할 때, 시스템은 물리적인 메모리를 프로그램에 직접 할당하지 않고 가상메모리라는 메모리 관리 기법을 이용하여 메모리 공간을 할당하게 된다.
가상메모리란 물리적인 메모리보다 큰 프로세스를 실행시키기 위해 물리 메모리를 추상화하는 기법으로, 메모리 관리의 핵심 중 하나이다.
중요한 개념이지만 여기서는 이런 개념이 있다 정도로만 짚고 넘어가겠다.
아무튼 malloc 함수를 호출하면 시스템에서는 가상메모리를 요청하여 공간을 할당하고, free를 호출하면 이 할당한 공간을 해제하게 된다. 이에 대한 과정은 다음과 같다.
malloc 함수를 호출하면 커널에서 가상메모리를 요청한다
그리고 malloc에서 요청한 크기만큼 프로그램의 가상메모리에 매핑을 하게 된다.
free 함수를 호출하면 할당된 공간을 해제하는데, 해제된 메모리는 커널에 반환되지 않고 free-list라는 것으로 관리된다.
그리고 프로그램에서 메모리 할당에 대해서 재요청을 하면, 관리하던 free-list에서 재할당을 하게 되는 것이다.
다만 이는 시스템에 따라 달라지는 부분이 있다.
❗️ free는 정확히 할당된 공간의 시작주소가 아니면 free를 하지 않는다 포인터 변수 p에 malloc으로 공간의 시작주소를 넘겨주고 p의 주소를 연산하여 이동시킨다음 free를 하면 에러가 발생한다
비유하자면 다음과 같다.
호텔이 하나 있다고 하자. 이 호텔에는 호텔리어 A와 B가 있다. A라는 호텔리어는 손님에게 방을 안내해주고 해당 방의 키를 건네주는 역할을 한다 B라는 호텔리어는 손님이 방에서 나간다고 하면 그 방을 ‘빈 방'이라는 노트에 써놓는 역할을 한다. A는 새로운 손님이 오면 ‘빈 방’이라는 노트를 보고 써져있는 방들을 우선적으로 배정하게 된다. 이 때, A는 malloc, B는 free가 하는 역할인 것이다.

dangling pointer

위의 비유를 보면서 한가지 드는 의문이 있을 것이다
손님이 나갈 때 키는 누가 수거해가지?
손님이 가지고 있는 키를 수거하지 않으면 그 손님은 여전히 ‘빈 방'이라는 노트에 쓰여있는 방에 들어갈 수 있을 것이다. 따라서 손님의 키를 수거할 수 있는 C라는 호텔리어가 새로 필요하다.
이처럼 포인터 변수가 free된 공간의 주소를 여전히 갖고 있음으로써 그 공간에 접근이 가능한 문제를 dangling pointer라고 한다.
다음과 같은 코드가 있다고 하자
char *s; s = (char *)malloc(sizeof(char) * 10); sprintf(s, "hello"); printf("%s\n", s); free(s); printf("%s\n", s); // dangling pointer 발생
C
복사
dangling pointer를 알지 못한다는 전제하에, 예상되는 결과는 다음과 같을 것이다.
output: hello (null)
C
복사
하지만 실제 출력되는 결과는 다음과 같다
output: hello hello
C
복사
이는 free함수가 s가 가리키고 있는 메모리 공간을 free-list로 관리될 수 있게 도와줄 뿐, 메모리 공간을 직접 지우거나 s에 NULL 포인터를 대입하지 않기 때문이다.
따라서 free를 한 후에 바로 s를 호출하면 여전히 s는 free된 공간을 가리키고 있고, 그 공간에는 여전히 ‘hello’라는 데이터가 남아있는 것이다.
dangling pointer를 방지하려면 다음과 같이 해야한다
free(s); s = NULL; // NULL을 넣음으로써 s가 free된 공간을 // 가리키지 않도록 한다 printf("%s\n", s);
C
복사

free를 한 후 dangling pointer는 무조건 방지해야 하는가?

free를 한 뒤 무조건 해당 포인터에 NULL을 넣어야 하는 것은 아니다. dangling pointer의 핵심은 free된 공간을 다시 접근하려 하는 것이므로, 다시 접근 할 일이 없으면 굳이 방지 할 필요는 없다.
이런 경우에는 방지 하지 않아도 된다
함수의 지역 포인터 변수를 free하고 바로 종료하는 경우
해당 포인터 변수를 free한 후에 다시 쓸 일이 없을 경우
한 편, free를 한 뒤 방지해야 하는 위치에 대해서도 유의해야 한다
포인터 변수를 매개변수로 가져온 뒤, 그 함수 안에서 free를 하고 그 뒤에 NULL을 넣어도 함수 밖에서는 적용이 되지 않고 여전히 free된 공간을 가리키고 있다
프로그래밍을 배울 때, 으레 구현하게 되는 swap 함수를 생각하면 쉽다.
이 때는 함수 밖에서 포인터 변수의 값을 NULL로 바꿔주거나, 매개변수로 가져올 때 이중포인터로 가져와서 해당 포인터 변수의 값을 수정할 수 있게 해주어야 한다.

출처

malloc, free
dangling pointer

 혹시 잘못된 부분이 있다면 댓글로 지적 부탁드립니다!