성장하는 중 입니다?/자료구조
"pointer being freed was not allocated"의 늪에 빠졌습니다.
알엘알엘
2021. 7. 2. 17:28
이 포스트는 "윤성우의 열혈 자료구조"를 공부하면서 작성되었습니다.
우선 나는 macOS Big Sur 11.4 에서 Xcode 사용 중이다.
Ch03. 연결 리스트의 문제 03-2. NameCard 를 구현하던 중 free() 에서 에러를 만났다.
그리고 며칠동안 이 "pointer being freed was not allocated" 에러에서 빠져나오지 못했다.
처음에는 macOS 에서는 동적할당 하는데 다른점이 있다거나, 메모리가 자동으로 해제된다거나 하는 줄 알았다.
그래서 free() 부분을 지우고, 5. 남아있는 모든 사람의 전화번호 정보를 출력한다. 단계로 넘어갔는데
EXC_BAD_ACCESS 가 뜨면서 빌드에 실패하는 것이다. 이것은
해제된 개체에 액세스 (자동 해제된 객체를 액세스하는 경우 포함)
시스템 호출에 유효하지 않은 포인터를 전달
한 경우에 대한 결과라고 한다.
아무래도 코드에 문제가 있는 것 같아서 다시 책을 폈다. 그리고 발견했다.
"ArrayList.h" 에서 typedef int LData 부분을 typedef NameCard * LData 로 수정해야 한다.
pcard 를 int로 읽어오면 포인터 성질이 아닌 것이 되기 때문에 free() 함수로 메모리를 해제하는 것이 불가능 한 것 같았다.
... 이러면서 배우는거지 그래.
아래는 문제의 ArrayList.h 와 NameCard.c, NameCardListMain.c 이다.
// ArrayList_h
#ifndef __ARRAY_LIST_H__
#define __ARRAY_LIST_H__
#define TRUE 1
#define FALSE 0
#define LIST_LEN 100
#include "NameCard.h"
// 1. 선언하기
typedef NameCard * LData; // 리스트에 int형 데이터의 저장을 위한 선언
typedef struct __ArrayList // 배열기반 리스트를 정의한 구조체
{
LData arr[LIST_LEN]; // 리스트의 저장소인 배열
int numOfData; // 저장된 데이터의 수
int curPosition; // 데이터 참조위치를 기록
} ArrayList;
typedef ArrayList List; // List 는 배열 기반 리스트이다!
// typedef LinkedList List; // List 는 연결 기반 리스트이다!
void ListInit(List * plist); // ListInit : 초기화
void LInsert(List * plist, LData data); // LInsert : 데이터 저장
int LFirst(List * plist, LData * pdata); // LFirst : 첫 데이터 참조
int LNext(List * plist, LData * pdata); // LNext : 두 번째 이후 데이터 참조
LData LRemove(List * plist); // LRemove : 참조한 데이터 삭제
int LCount(List * plist); // LCount : 저장된 데이터의 수 반환
#endif
// NameCard.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "NameCard.h"
NameCard * MakeNameCard(char * name, char * phone)
{
// NameCard 구조체 변수의 동적 할당 및 초기화 후 주소 값 반환
NameCard * newCard = (NameCard*)malloc(sizeof(NameCard));
strcpy(newCard->name, name);
strcpy(newCard->phone, phone);
return newCard;
}
void ShowNameCardInfo(NameCard * pcard)
{
// 구조체 변수의 정보 출력
printf("보여줄 네임카드의 이름은 %s 이고, 전화번호는 %s 입니다. \n", pcard->name, pcard->phone);
}
int NameCompare(NameCard * pcard, char * name)
{
// 이름이 같으면 0, 다르면 0이 아닌 값 반환
return strcmp(pcard->name, name);
}
void ChangePhoneNum(NameCard * pcard, char * phone)
{
// 전화번호 정보를 변경
strcpy(pcard->phone, phone);
}
// NameCardListMain.c
#include <stdio.h>
#include <stdlib.h>
#include "ArrayList.h"
#include "NameCard.h"
int main(void)
{
List list;
NameCard * pcard;
ListInit(&list);
// 1. 총 3명의 전화번호 정보를, 앞서 우리가 구현한 리스트에 저장한다.
// card.name, card.phone 을 리스트에 저장
pcard = MakeNameCard("양희은", "010-1111-1111");
LInsert(&list, pcard);
pcard = MakeNameCard("윤준피", "010-2222-2222");
LInsert(&list, pcard);
pcard = MakeNameCard("정재원", "010-3333-3333");
LInsert(&list, pcard);
// 2. 특정 이름 "정재원"을 대상으로 탐색을 진행하여, 그 사람의 정보를 출력한다.
if(LFirst(&list, &pcard))
{
if(!NameCompare(pcard, "정재원"))
ShowNameCardInfo(pcard);
else
{
while(LNext(&list, &pcard))
{
if(!NameCompare(pcard, "정재원"))
{
ShowNameCardInfo(pcard);
break;
}
}
}
}
// 3. 특정 이름을 대상으로 탐색을 진행하여, 그 사람의 전화번호 정보를 변경한다.
if(LFirst(&list, &pcard))
{
if(!NameCompare(pcard, "윤준피"))
ChangePhoneNum(pcard, "010-9999-9999");
else
{
while(LNext(&list, &pcard))
{
if(!NameCompare(pcard, "윤준피"))
{
ChangePhoneNum(pcard, "010-9999-9999");
break;
}
}
}
}
// 4. 특정 이름을 대상으로 탐색을 진행하여, 그 사람의 NameCard 주소값을 list에서 삭제한다.
if(LFirst(&list, &pcard))
{
if(!NameCompare(pcard, "정재원"))
{
// LRemove 는 rdata 를 반환한다. rdata 는 list의 arr[rpos]값으로, 즉 삭제된 NameCard의 주소값을 의미함. 이걸 pcard 에 넣음.
// 삭제된 NameCard 의 주소값이 heap에 동적 할당되어 있었을까? 그러면 free 로 반환이 가능.
pcard = LRemove(&list);
free(pcard);
}
else
{
while(LNext(&list, &pcard))
{
if(!NameCompare(pcard, "정재원"))
{
pcard = LRemove(&list);
free(pcard);
break;
}
}
}
}
// 5. 끝으로, 남아있는 모든 사람의 전화번호 정보를 출력한다.
if(LFirst(&list, &pcard)){
ShowNameCardInfo(pcard);
while(LNext(&list, &pcard))
ShowNameCardInfo(pcard);
}
return 0;
}