성장하는 중 입니다?/자료구조

"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;
}