malloc() 을 이용한 메모리 할당 관련 - malloc()호출이 기존의 연결 리스트 영역에 영향을 미칠 수 있나요? ( 테스트한 내용 추가)
제목 : malloc() 을 이용한 메모리 할당 관련 - 연결 리스트의 마지막 요소에 저장된 내용이 사라짐
연결 리스트의 내용을 출력하는 함수가 있습니다.
int print_list(addr_list_entry *list_head)
{
addr_list_entry *entry=(addr_list_entry *)malloc(sizeof(addr_list_entry));
entry=list_head->next;
printf("\n");
while(entry->next != NULL)
{
printf("%s\n",entry->url);
entry=entry->next;
}
free(entry);
return 0;
}
addr_normalizer() 라는 함수에서 print_list() 를 호출해, 인자로 전달받은 연결 리스트의 내용을 확인합니다.
addr_normalizer() 의 간략화된 코드는 아래와 같습니다.
normalized_url *addr_normalizer(addr_list_entry *addr_list_head)
{
(생략된 거 없음)
normalized_url *normalized_list=(normalized_url *)malloc(sizeof(normalized_url));
print_list(addr_list_head);
(생략)
}
위와 같은 코드를 실행시켰는데..
연결리스트의 마지막 요소의 내용이 안 나옵니다. 아래 수행 화면을 보시면 맨 마지막 entry 의 내용이 비어있는 걸 확인하실 수 있습니다.
(entry : 210200)(entry -> url : 210208)== (공란) 부분입니다.
(틀린 수행 결과)
이것 저것 해 보다가 print_list() 를 아래와 같이 malloc() 함수 호출 이전에 호출해 봤습니다.
normalized_url *addr_normalizer(addr_list_entry *addr_list_head)
{
(생략된 것 없음)
print_list(addr_list_head);
normalized_url *normalized_list=(normalized_url *)malloc(sizeof(normalized_url));
(생략)
}
그랬더니 아래와 같이 정상적으로, 연결 리스트의 끝요소의 내용까지 출력이 됩니다.
역시 (entry : 210200)(entry -> url : 210208)== (URL 주소) 부분입니다
(옳바른 수행 결과)
malloc() 과 관련해 뭔 일이 있는 거 같은데... 그게 뭔지 잘 모르겠습니다..
왜 이런 일이 생기는 걸까요?
----------------------------------(아래가 테스트 추가분)-------------------------------------------
malloc() 의 수행이 기존에 잡혀있던 연결 리스트의 영역을 건드리는 건 아닌지 의심하고 있습니다.
그런 일이 가능할까요?
다음은 제가 추가로 해 본 건데요..
(사례 1)
normalized_url *addr_normalizer(addr_list_entry *addr_list_head)
{
(생략된 것 없음)
print_list(addr_list_head); (1) -->맨 마지막 요소가지 출력
normalized_url *normalized_list=(normalized_url *)malloc(sizeof(normalized_url));
print_list(addr_list_head); (2) -->맨 마지막 요소가 출력 안 됨
(생략)
}
(결과) (1)번 print_list() : 맨 마지막 요소까지 출력 , (2)번 print_list() : 맨 마지막 요소가 출력 안 됨.
(사례 2)
normalized_url *addr_normalizer(addr_list_entry *addr_list_head)
{
(생략된 것 없음)
print_list(addr_list_head); (1) -->맨 마지막 요소까지 출력
normalized_url *normalized_list=NULL;
print_list(addr_list_head); (2) -->맨 마지막 요소까지 출력
(생략)
}
(결과) (1),(2)번 print_list() 둘 다 맨 마지막 요소까지 출력
malloc() 호출 부분을 없애고, NULL 로 바꾸니까 print_list()가 호출 위치에 상관없이 성공적으로 수행되는 거 같아서 저는 프로그램의 다른 부분에는 문제가 없다고 생각하고 있습니다. malloc() 호출때문에 기존 연결 리스트의 메모리 공간이 훼손(?)되는 건 아닌가 하는 생각이 드는데.. 맞는 추측일까요?
또한 결과 화면을 보시면 각 요소에 대해 entry 와 entry->url 의 주소를 괄호 안에 출력하게끔 했는데.. '틀린결과'와 '옳은결과'의 문제가 되는 맨 마지막 요소를 보시면 (entry->url )의 주소가 다릅니다. '옳은 결과'화면의 경우 (entry->url) 부분의 주소값이 맨 마지막 요소까지 208씩 일정하게 증가하는 모습을 보이는데.. '틀린 결과'화면의 경우 맨 마지막 (entry->url) 주소값이 그 직전까지의 (entry->url) 주소값들과는 전혀 동떨어진 곳에 할당이 되는 걸로 나옵니다. 여기에도 뭔가가 있는 거 같은데.. 전 잘 모르겠네요..
malloc() 을 이용해서 잡힌 메모리 주소가 따로 있는데... 기존의 연결 리스트의 메모리 공간이 영향을 받는다... 이게 맞는 추측인지... 만약 그렇다면 왜인지... 어떻게 수정할 수 있는지 알려주세요...
아래는 addr_list_entry 구조체와 normalized_url 구조체입니다. (참고용)
struct link_addr
{
char *url;
struct link_addr *next;
};
typedef struct link_addr addr_list_entry;
struct normalized_link_addr
{
char *target_site;
char *location;
struct normalized_link_addr *next;
};
typedef struct normalized_link_addr normalized_url;
첨부 | 파일 크기 |
---|---|
wrong.JPG | 137.03 KB |
correct.JPG | 111.82 KB |
/* ... */addr_list_entry
이상하지 않으세요?
어떤 부분 말씀하시는건지 모르겠습니다...^^;;
답변 감사합니다..^^
malloc() 이용해서 주소 할당해 주고 list_head->next 를 써서 다른 주소 할당해 준 거 말씀하신 거 맞지요? ^^;;
말씀해 주신 부분을
addr_list_entry *entry=list_head->next;
로 고쳐서 해봤는데 동일한 결과가 나옵니다...
list_head->next가 아니라
list_head->next가 아니라 list_head부터 시작해야 하는 것 같은데요?
그리고, while 문에서도 entry != NULL일때로 고치면 될 것 같네요.
다음과 같이 하면 될 듯...
말씀하신대로 해봤는데..
안 되네요..
그런데.. list_head 에는 내용이 없기 때문에 제가 작성한 방식이 맞다고 생각합니다만...
답변 감사합니다..^^
저도 ditto님이랑 같은
저도 ditto님이랑 같은 생각을 했는데요. 혹시 list entry들을 allocation하고, url을 집어넣는 부분도 보여주실 수 있으신가요?
----
Let's shut up and code.
----
Let's shut up and code.
리스트의 내용을
리스트의 내용을 출력하는 함수에서
malloc()을 할 필요가 있나요?
그리고 leak도 생기네요.
malloc()부분은 위에서
malloc()부분은 위에서 다른 분이 말씀하셔서 고치신 것 같습니다 ^^
물론 free() 부분도 지우셨을 거 같구요..
----
Let's shut up and code.
----
Let's shut up and code.
연결 리스트 조작 함수입니다..^^
연결 리스트에 url 집어넣는 insert_addr 함수입니다... 웹문서내의 링크 주소를 수집해서 각 링크 주소에 대해 insert_addr 함수를 호출합니다. return 값으로 반환되는 addr_list_entry * 는 다음번 insert_position 을 나타내기 위해 쓰입니다. 저는 연결 리스트의 마지막 위치 (tail 직전)에 새로운 요소를 삽입하려고 합니다.
addr_list_entry *insert_addr(char *retrieved_url,addr_list_entry *insert_position)
{
addr_list_entry *new_entry=NULL;
char *for_url;
new_entry=(addr_list_entry *)malloc(sizeof(addr_list_entry));
for_url=(char *)malloc(MAX_URL_SIZE);
if(new_entry==NULL || for_url==NULL)
{
inform("Memory allocation failed in addr_list.c:insert_addr()");
return insert_position;
}
//Clear memory space before use
memset(new_entry,'\0',sizeof(addr_list_entry));
memset(for_url,'\0',MAX_URL_SIZE);
//------------------------------
//Set url value of structure
//------------------------------
strcpy(for_url,retrieved_url);
new_entry->url=for_url;
new_entry->next=insert_position->next; //Attach address to the end of address list
insert_position->next=new_entry;
return new_entry;
}
-------------------------------------------------------------
연결 리스트 초기화, 삭제, 인쇄하는 함수도 참고하시라고 올려봅니다.
addr_list_entry *init_addr_list(void)
{
addr_list_entry *head=(addr_list_entry *)malloc(sizeof(addr_list_entry));
addr_list_entry *tail=(addr_list_entry *)malloc(sizeof(addr_list_entry));
//Clear memory space before use
memset(head,'\0',sizeof(addr_list_entry));
memset(tail,'\0',sizeof(addr_list_entry));
//Create linked list
head->next=tail;
tail->next=NULL;
return head;
}
int clear_addr_list(addr_list_entry *head)
{
addr_list_entry *for_free=(addr_list_entry *)malloc(sizeof(addr_list_entry));
addr_list_entry *for_check=(addr_list_entry *)malloc(sizeof(addr_list_entry));
if((for_free==NULL) || (for_check==NULL))
{
fatal("Failure of memory allocation in clear_addr_list()");
}
for_check=head->next;
while(for_check->next != NULL)
{
for_free=for_check;
for_check=for_check->next;
free(for_free->url);
free(for_free);
memset(for_free->url,'\0',MAX_URL_SIZE);
memset(for_free,'\0',sizeof(addr_list_entry));
}
free(for_free);
free(for_check);
free(head);
inform("clearing address list completed");
return 0;
}
int print_list(addr_list_entry *list_head)
{
#if 0
addr_list_entry *entry=(addr_list_entry *)malloc(sizeof(addr_list_entry));
entry=list_head->next;
#endif
addr_list_entry *entry=list_head->next;
printf("\n");
while(entry->next != NULL)
{
printf("%s\n",entry->url);
entry=entry->next;
}
// free(entry);
return 0;
}
코드에 메모리릭과 손 좀 봐야할 곳이 좀 보이는데....
addr_list_entry *insert_addr(char *retrieved_url,addr_list_entry *insert_position)
{
addr_list_entry *new_entry=NULL;
char *for_url;
new_entry=(addr_list_entry *)malloc(sizeof(addr_list_entry));
// for_url을 MAX_URL_SIZE 만큼 할당 받을 필요가 있으신가요?
// 항상 MAX_URL_SIZE 만큼 할당을 받으실것이라면,
// addr_list_entry 의 struct에서 url을 char[MAX_URL_SIZE]로 하시는게 더 나을듯 한데...
// 보통은 malloc( strlen(retrieved_url) + 1 ) 만큼 할당받는게 malloc을 쓰는 의미가 있을듯 합니다.
// 아래에서 strcpy를 사용하는데, 만일 strlen(retrieved_url) > MAX_URL_SIZE 라면, overflow가 발생할 수 있습니다.
for_url=(char *)malloc(MAX_URL_SIZE);
if(new_entry==NULL || for_url==NULL)
{
inform("Memory allocation failed in addr_list.c:insert_addr()");
return insert_position;
}
//Clear memory space before use
memset(new_entry,'\0',sizeof(addr_list_entry));
// 위에서 만일 malloc( strlen(retrieved_url) + 1 )로 하셨다면,
// 이 부분의 MAX_URL_SIZE도 strlen(retrieved_url) + 1 로 변경하시길...
memset(for_url,'\0',MAX_URL_SIZE);
//------------------------------
//Set url value of structure
//------------------------------
strcpy(for_url,retrieved_url);
new_entry->url=for_url;
// insert_position->next 가 항상 NULL이라는것을 보장하는지 모르겠습니다.
// 저라면.. new_entry->next = NULL; 이라고 쓸듯...
new_entry->next=insert_position->next; //Attach address to the end of address list
insert_position->next=new_entry;
return new_entry;
}
int clear_addr_list(addr_list_entry *head)
{
// 아래의 pointer들은 malloc을 해줄 필요가 없습니다.
// head로 부터 pointing만 하기에...
// addr_list_entry *for_free,*for_check; 만 해주세요.
addr_list_entry *for_free=(addr_list_entry *)malloc(sizeof(addr_list_entry));
addr_list_entry *for_check=(addr_list_entry *)malloc(sizeof(addr_list_entry));
// 아래의 if 부분도 필요 없습니다. malloc을 할 필요가 없기에.
if((for_free==NULL) || (for_check==NULL))
{
fatal("Failure of memory allocation in clear_addr_list()");
}
// 만일, malloc을 하셨다면, 여기 부분에서 leak이 발생해 버립니다.
// 위에서 malloc을 제거하고 아래의 부분은 남겨두세요.
for_check=head->next;
// 아래의 while 구문을
// while( for_check != NULL ) 로 바꾸세요.
// 처음 들어왔을때, for_check->next는 head->next->next가 되어 버려서,
// 제일 앞에 있는 list는 무시가 될것입니다.
while(for_check->next != NULL)
{
for_free=for_check;
for_check=for_check->next;
free(for_free->url);
free(for_free);
// free를 해주었는데, memset을 하면 어떻게 동작할지 알 수 없습니다.
// memset을 할 필요가 없으니, 삭제하시구요.
memset(for_free->url,'\0',MAX_URL_SIZE);
memset(for_free,'\0',sizeof(addr_list_entry));
}
// 위에서 free해준 것을 또 free 하기에, 원하는대로 동작하는것을 보장 할 수 없습니다.
// 메모리가 꼬이게 되는 것이지요..
// 아래의 두줄은 삭제하세요.
free(for_free);
free(for_check);
// 아래의 이것만 남기세요.
free(head);
inform("clearing address list completed");
return 0;
}
원래의 위의 함수를 사용한다면, 메모리는 꼬여서 바보가 되어 있을 것입니다.
int print_list(addr_list_entry *list_head)
{
#if 0
addr_list_entry *entry=(addr_list_entry *)malloc(sizeof(addr_list_entry));
entry=list_head->next;
#endif
addr_list_entry *entry=list_head->next;
printf("\n");
// 아래의 while 구문을
// while( entry != NULL ) 로 바꾸세요.
// entry = entry->next; 로 해 놓고,
// while에서 entry->next != NULL 로 비교를 하면,
// 중간것을 빼먹게 될 것입니다.
while(entry->next != NULL)
{
printf("%s\n",entry->url);
entry=entry->next;
}
// free(entry);
return 0;
}
우선 올려주신 코드에서 제 나름(?)대로 문제가 될만한걸 정리해 드리긴 했는데요....
다른 코드들에서도 비슷한 상황(?)이 있을 듯 합니다.
추가로 init_addr_list 가 이상합니다.
addr_list_entry *init_addr_list(void)
{
addr_list_entry *head=(addr_list_entry *)malloc(sizeof(addr_list_entry));
// tail은 없어야 마땅할 것 같은데요?
// 초기화 하면, head가 tail이 되어야 할텐데,
// 아래에서 head->next = tail 이라고 해주면, head는 더이상 tail이 아닙니다.
// 이 함수에서 리턴하는 값이 tail을 가리키는 pointer가 되어야 하는데,
// tail이면 tail->next가 NULL이어야 할테니까요.
// 초기화 한 이후에, head->next가 NULL이 되어야 하는게 맞지 않을까요?
// 아래 줄은 삭제하세요.
addr_list_entry *tail=(addr_list_entry *)malloc(sizeof(addr_list_entry));
//Clear memory space before use
memset(head,'\0',sizeof(addr_list_entry));
// 아래 줄도 삭제하시구요.
memset(tail,'\0',sizeof(addr_list_entry));
//Create linked list
// head->next = NULL 로 해주시고,
head->next=tail;
// 아래 줄은 삭제하세요.
tail->next=NULL;
return head;
}
말씀하신대로 함수를 고쳐봤는데요.. 여전히 안 됩니다..
일일이 손 봐 주셔서 감사합니다.^^
지적해 주신 내용 보고, 관련 함수를 다시 손 봤는데.. 역시 안 됩니다...
(참고로, 첨부한 파일이 제가 수정한 관련 함수들입니다.)
그런데 이상하지 않나요?
함수를 잘못 작성한 거면 일관되게 마지막 요소가 빠진채로 나와야 할 텐데.. 아래와 같이
malloc()
print_list()
와 같은 호출에선 연결 리스트의 마지막 요소 하나가 빠진채로 출력되고..
print_list()
malloc()
과 같은 호출에선 연결 리스트의 끝까지 다 출력이 된다니...
malloc() 과 관련해 무슨 문제가 있는 거 아닐까 하는 생각이 드는데... 그게 뭔지 도대체 모르겠습니다.
연결 리스트 처리에 잘못된 곳이 있기 때문입니다.
안녕하세요.
단일 연결 리스트를 구현하셨는데 메모리를 두번 해제하거나 해제된 메모리 공간에
데이터를 지우거나 하면 heap이 오염됩니다. 결국 아무 관련 없는 코드가 중간에 삽
입되더라도 random한 동작을 하기도 합니다. (실제로는 random하지는 않습니다만
그렇게 보일 뿐입니다. malloc 한줄로 결과가 바뀌었다고 하시는데요 malloc 함수는
heap의 정보가 정상적이라고 가정한 상태에서 작성된 메모리 할당 함수입니다. heap의
정보가 비정상이라면 기존에 할당된 정보도 손실될 수 있는거죠. 현재의 상황은 그런
상황으로 보입니다. malloc을 수행한 것이 디버깅에 도움이 되는 힌트를 준 상황이죠.)
실제로 당해보기 전에는 메모리 관리 잘못한 걸로 저런 오류가 나온다는 걸 모릅니다.
현재까지의 동작 순서대로 함수를 다시 호출해서 링크가 일일이 잘못되는 곳이 없는지
step-by-step debugging을 해보시길 권합니다. 또, 다른 곳에도 같은 알고리즘을 사용
했다면 역시 버그가 있을 것이므로 다른 자료 구조도 다시 검사해 보시기 바랍니다.
그럼...
감사합니다..^^
말씀하신 것 듣고..
제 코드에서 free() 가 정확하게 수행이 됐는지 유심히 살펴봤는데..
삽입을 위해 사용한 insert_position 이라는 포인터 변수의 공간에 대해 free() 를 수행하고 있었습니다.
즉, tag_eliminator() 에서 insert_position 다음에 새로운 요소를 연결 리스트에 삽입해 주는데..
삽입 요소를 나타내기 위해 사용한 insert_position 이라는 포인터 변수를 위해 처음에 malloc() 을 이용해 공간을 할당해 줬습니다. (tag_elimiantor() 말미에, 당연히 free() 를 이용해 공간을 해제해 줬구요..)
insert_position 은 항상 연결 리스트의 맨 마지막 요소를 가리키게끔 되어 있었는데.. free(insert_position) 을 수행했으니.. 마지막 요소에 대한 메모리 공간이 해제된 겁니다...
저는 임시로 사용한 포인터니까 할당된 공간을 해제해야겠다는 의도로 쓴 것인데...
애당초 malloc() 을 이용해서 공간을 할당해 주는 게 아니었던 거 같습니다.
감사합니다...^^ 이런 실수로 인해 며칠간 고생하다니... 허망하고 약간 부끄럽네요...^^;;;
질문하신 내용과는
질문하신 내용과는 별상관이 없지만
free(entry);
부분이 잘못된 것 같습니다.
저 코드대로라면 free 를 호출하는 시점에서 entry 는 리스트의
마지막을 가리키게 됩니다. 즉 print_list 함수를 호출할 때마다
마지막 entry를 반복적으로 없애게 됩니다.
(null 을 할당한 것은 아니므로 코드는 잘 동작하는 듯 하다가
우연히 다음 malloc 호출이 그 위치에 메모리 할당을 하면
프로그램이 오동작하게 됩니다.)
그런데 주석 처리를 해도..
malloc 과 free 를 없애고 수행해도 같은 결과가 나옵니다...
소스를 보니
소스를 보니 의심스러운 부분이 좀 있네요.
clear할 때도 malloc()을 하는 것도 이상하고...
일반적인 list 구현과는 달라보입니다.
혹시 leak은 안생기던가요?
다른 list 구현 소스를 참고하시는 게 좋겠습니다.
malloc() 관련한 테스트 결과 원문에 추가했습니다.
답변들 감사합니다.
malloc() 관련한 테스트 결과 원문에 추가했습니다.
답 주실 때 참고해 주세요..^^
entry->url 의 맨 마지막 주소값이 전혀 다른 곳에 생기는 게 제일 의심스럽습니다
정상적으로 수행될 경우, entry->url 의 주소값은 208씩 증가 ,entry 의 주소값은 16씩 증가
비정상적으로 수행될 경우, entry->url 의 주소값이 208씩 증가하다가 맨 마지막 요소의 entry->url 주소값만 전혀 다른 곳에 생성. 그리고 그 맨 마지막 요소의 entry->url 주소값은 맨 마지막 요소의 entry 주소값에 8만큼을 더한 수치 (몇 번을 수행해도 같은 결과가 나옴)
맨 마지막 요소 이전까지는 entry 랑 entry->url 이랑 큰 상관이 없는 것처럼 보이는데..
addr_normalizer() 에서 malloc() 을 수행해 normalized_url * 의 변수를 선언하기 이전 print_list() 호출시에는 정상적으로 수행되다가.. 선언 이후 print_list() 수행시엔 맨 마지막 요소의 entry->url 에 있던 내용이 지워짐..
문제가 있다면 연결 리스트의 초기화나 삽입, 인쇄 함수에 있는듯.. (연결 리스트 삭제 함수는 인쇄 함수 이후에 호출되므로..)
뭐 대략 제 생각의 흐름은 이렇습니다...^^;;
궁금한게...
printf("%s\n",entry->url);
이렇게만 했는데..
(entry : 210200)(entry -> url : 210208)== (URL 주소)
이런 형식으로 출력이 되나요? 다른 형식으로 printf 문이 있는 것 같은데요...
이상하게 나오는 화면이 나올때 쓰던 소스를 그대로 좀 보여주셨으면 합니다만...
/* How to Love Others */
while(GetDepth(Love) < Enough) DoLove();
테스트한 부분은 삭제한 코드를 올렸습니다.
실제 코드엔 아래 부분이 들어가 있습니다.
//Test -----------------
printf("(entry : %d)(entry->url : %d)==",entry,entry->url);
//Test -----------------
그리고 이 바로 밑에
printf("%s\n",entry->url);
이 나옵니다.
이 외에도 테스트용으로 집어넣은 코드들은 뺀 채로 올렸습니다.
다 올려봤자, 알아보기만 힘들 것 같아서요...^^;;
malloc() 이후에 인자로 건네받은 연결 리스트의 내용이 변경되는 것이라면..
가공하는 이후 과정에서 다시 에러가 생길 것 같아서..
지금 고치려고 하는데.. 도저히 감이 안 옵니다.
malloc() 호출이 연결리스트 생성시 수행됐던 이전 malloc() 호출과 전혀 상관 관계는 없을 것 같은데... 왜 이런 일이 생기는 걸까요?
참고하시라고, print_list() 호출 부분이 있는 addr_normalizer.c 함수 첨부합니다.
해결했습니다.
제 코드에서 free() 가 정확하게 수행이 됐는지 유심히 살펴봤는데..
삽입을 위해 사용한 insert_position 이라는 포인터 변수의 공간에 대해 free() 를 수행하고 있었습니다.
즉, tag_eliminator()라는 함수에서 insert_position 다음에 새로운 요소를 연결 리스트에 삽입해 주는데..
삽입 요소를 나타내기 위해 사용한 insert_position 이라는 포인터 변수를 위해 처음에 malloc() 을 이용해 공간을 할당해 줬습니다. (tag_elimiantor() 말미에, 당연히 free() 를 이용해 공간을 해제해 줬구요..)
insert_position 은 항상 연결 리스트의 맨 마지막 요소를 가리키게끔 되어 있었는데.. free(insert_position) 을 수행했으니.. 마지막 요소에 대한 메모리 공간이 해제된 겁니다...
저는 임시로 사용한 포인터니까 할당된 공간을 해제해야겠다는 의도로 쓴 것인데...
애당초 malloc() 을 이용해서 공간을 할당해 주는 게 아니었던 거 같습니다.
답변 주셨던 모든 분들께 감사드리고 싶습니다..^^
이런 사소한 실수로 인해 며칠간 고생하다니... 허망하고 좀 부끄럽네요...^^;;;
댓글 달기