c언어 동적할당 해제시 오류 질문 드립니다.

kokoroo의 이미지

구조체 포인터를 함수로 넘겨서 구조체 멤버변수들을 바꾸는 예제입니다.

함수 func2 만 보셔도 무방합니다.

소스코드---------------------------------

#include <stdio.h>
#include <stdlib.h>
 
typedef struct hello {
 
	int x;
	int y;
	int* px;
	int* py;
}HELLO;
 
void func2(HELLO* hello);
 
int main() {
 
	HELLO hello1;
 
	hello1.x = 10;
	hello1.y = 20;
	hello1.px = &hello1.x;
	hello1.py = &hello1.y;
 
	printf("x의값 : %d\n", hello1.x);
	printf("x의주소 : %x\n", &hello1.x);
 
	printf("y의값 : %d\n", hello1.y);
	printf("y의주소 : %x\n\n", &hello1.y);
 
	printf("px의값 : %x\n", hello1.px);
	printf("px의주소 : %x\n", &hello1.px);
	printf("px가 가리키는 값 : %d\n", *hello1.px);
 
	printf("py의값 : %x\n", hello1.py);
	printf("py의주소 : %x\n", &hello1.py);
	printf("py가 가리키는 값 : %d\n\n\n", *hello1.py);
 
	HELLO* phello = &hello1;
 
	func2(phello);
 
	printf("x의값 : %d\n", hello1.x);
	printf("x의주소 : %x\n", &hello1.x);
 
	printf("y의값 : %d\n", hello1.y);
	printf("y의주소 : %x\n\n", &hello1.y);
 
	printf("px의값 : %x\n", hello1.px);
	printf("px의주소 : %x\n", &hello1.px);
	printf("px가 가리키는 값 : %d\n", *hello1.px);
 
	printf("py의값 : %x\n", hello1.py);
	printf("py의주소 : %x\n", &hello1.py);
	printf("py가 가리키는 값 : %d\n\n\n", *hello1.py);
 
	return 0;
}
 
 
void func2(HELLO* hello) {
 
	hello->x = 1;
	hello->y = 2;
	int* temp = (int*)malloc(sizeof(int));
 
 
 
	temp = hello->px;
	hello->px = hello->py;
	hello->py = temp;
 
//	temp = NULL;
	free(temp);
 
}

----------------------------------

이때 free(temp); 부분에서 runtime 에러가 납니다.

아예 free(temp); 를 빼서 해제를 하지 않거나,
주석부분인 temp = NULL; 을 적어서 해제전에 NULL포인터로 만들면 에러가 나지 않습니다.

하지만 이것들은 편법인것 같고, free(temp); 다음에 temp = NULL; 을 적으면 에러가 나네요..
temp = NULL;을 적지않고 free(temp);만 적어도 에러가나구요.

할당해제시 에러가 나는 이유가 무엇일까요?

익명 사용자의 이미지

free할 때에 temp는 func2가 호출되던 때에 hello->px 가 가지고 있던 값입니다 (왜냐하면 temp = hello->px). 그 주소에는 메모리를 동적으로 할당한 적이 없으니까 당연히 에러가 납니다. func2 에서 temp에 할당한 메모리를 해제하고 싶으시면 다른 포인터를 하나 더 써서 temp를 복사해두었다가 free에게 넘겨야합니다.

> 아예 free(temp); 를 빼서 해제를 하지 않거나, 주석부분인 temp = NULL; 을 적어서 해제전에 NULL포인터로 만들면 에러가 나지 않습니다. 하지만 이것들은 편법인것 같고, ...

편법이 아니고, 그러면 안됩니다. 메모리 누수 입니다. 버그에요.

kokoroo의 이미지

1. temp라는 포인터가 hello->px 포인터가 가리키는 값과 같은 곳을 가리키고 있었다 하더라도, 동적 할당된 temp만을 free함수로 해제시키는 것이 왜 잘못된 것인지 모르겠습니다.

2. 다른 포인터를 쓴다는 것이 int* ptr = temp를 한 후에 free(ptr)을 하는 것이 맞나요?

3. 다른포인터를 써서 temp를 복사해둔 후, free에 넘기면 다른 포인터만 해제되는 것이 아닌가요?

4. 해제를 하지 않으면 메모리 누수인 것은 이해가 됩니다. temp = NULL; 로 바꿔준 다음에 free(temp)를 하는 것도 메모리 누수인가요?

답변 정말 감사합니다. C언어 너무 어렵네요 ㅠㅠ

raymundo의 이미지

제가 원 답변 올리신 분은 아니지만 지나가다 본 김에...

free(어떤 변수);
 
+------+     +------------------------------+
| 변수 +---->| 할당받은 공간(free로 해제될) |
+------+     +------------------------------+

free는 "어떤 변수를" 해제하는 게 아닙니다. "그 변수가 가리키고 있는, 즉 변수에 담겨 있는 주소값부터 시작하고 있는" 공간을 해제하는 거죠. 그리고 그 주소는 malloc(또는 calloc, realloc)을 호출하여 리턴값으로 받은 주소여야 하고요.

1.

본문의 temp는 그저 int * 타입의 지역변수일 뿐입니다.
처음에는 malloc으로 할당된 공간을 가리키고(즉 그 공간의 시작 주소를 담고) 있었지만, hello->px 를 대입하는 순간부터 hello->px가 가리키고 있던, 이 경우 hello1.x 의 주소를 담게 되었습니다.

따라서 free는 hello->px 가 가리키는 공간을 해제하려고 하지, 의도하신 대로 "동적 할당된 temp만"(사실 이것도 잘못된 말이고, 동적할당되어 temp가 가리키고 있던 공간을이라고 써야겠지만)" 해제하지 않습니다.

그 동적할당된 공간은 이제는 해제하려고 해도 할 방법조차 없습니다. 그 주소를 누구도 모르니까요. 유일하게 그 주소를 담고 있던 게 temp 변수인데 이제는 temp에도 다른 값이 들어가 있어서요. 프로그램이 종료될 때까지, 저 공간은 할당받아놓고는 쓰지 못하게 된 공간으로 낭비됩니다.

2.

네 맞습니다. 물론 hello->px 를 대입하기 "전"에 복사해야 합니다.

3.

이제는 3번 질문이 왜 잘못된 건지 아셨을 겁니다.

4.

앞서 말했듯이 temp에 다른 값이 들어가면(NULL이든 뭐든) 이제 더 이상 원래 할당받았던 공간을 해제할 방법이 없으니 누수가 일어납니다. temp = NULL을 하고 해제했을 때 에러가 안 나는 건 그저 free가 인자로 널 포인터를 받으면 그냥 아무 일도 하지 않고 끝나기 때문입니다.

끝으로...

하려는 일이 그저 px 포인터와 py 포인터의 스왑이라면, 애초에 별도의 공간을 할당받을 필요조차 없습니다. px의 값을 임시로 저장할 공간은 temp이지, temp가 가리키는 다른 어딘가가 아니니까요.

int *temp = hello->px;
hello->px = hello->py;
hello->py = temp;

좋은 하루 되세요!

kokoroo의 이미지

가슴 한 가운데 막혔던 것이 모두 뚫리는 기분입니다.

공부 열심히 하겠습니다. 진짜로 감사합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.