자료구조 코딩 시 주의사항
이전 게시물에서 그대로 가져온 [ 클래스를 통한 표현 ]의 코드이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | #include <iostream> using namespace std; class node { private: int data; node *next; public: void inputData(int value) { data = value; } void init() { next = NULL; } void doNext(node *newnode) { next = newnode; } void see(node *head) { node *pos = head; int val = pos->data; while (1) { cout << val << " "; if (pos->next == NULL) { pos = NULL; // 이 과정이 없다면 delete pos시 연결 리스트 마지막 값 메모리가 해제 되어버린다. delete pos; break; } pos = pos->next; val = pos->data; } cout << endl; } }; int main() { node *head = NULL; // 이것은 구조체를 만든 것이 아닌 node 구조체를 가리킬 수 있는 포인터를 만든 것이다. node *tail = NULL; // node node1; 과는 다르다. node *cur = NULL; node *newnode = NULL; int value; while (1) { cout << "자연수를 입력해 주세요 : " ; cin >> value; if (value < 1) // 예외처리는 모든 코딩에서 아주 중요하다. { cout << "다시 입력해 주세요."<< endl; head->see(head); // 연결 리스트가 잘 연결되고 있는지 한번 확인해 본다. (이때는 첫부분인 head를 기점으로 본다.) continue; } // 노드의 추가 과정 newnode = new node; // node의 크기만큼 newnode에 공간을 할당해 준다. newnode->inputData(value); // 새로 생긴 node에 data를 넣는다. newnode->init(); // 새로 생긴 node의 next 포인터는 NULL을 가리키도록 한다. if (head == NULL) head = newnode; // 만약 head가 아무것도 가리키지 않고있다면 newnode를 가리키게 한다. else tail->doNext(newnode); // 그렇지 않다면 tail의 next 즉, tail은 node를 가리키니 node의 next가 newnode를 가리키게 한다. tail = newnode; } return 0; } | Crocus |
이 코드에서 한번 생각해 보아야 할 곳이 있다.
1. node *head; node *cur; node *tail; 에는 data영역과 next영역이 있을까?
정답은 없다이다. 물론 포인터에 대해 자주 접해본 코더는 이러한 질문에 대해 당연하다고 생각할 것이다.
하지만 자료구조나 포인터 같은 개념이 아직 많이 확립되지 못한 코더들은 이 부분에서 고민을 했을 수 있다.
node *head, node *cur, node *tail은 new나 malloc을 통해 공간을 할당 받지 않은 단순한 node 클래스를 가리킬 수 있는 포인터이라는 것이다.
이때 int *head, char *head도 포인터니 node 클래스를 가리킬 수 있지 않을까 생각해 볼 수도있지만, 같은 type이어야만 포인터가 가리킬 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void see(node *head) { node *pos = head; int val = pos->data; while (1) { cout << val << " "; if (pos->next == NULL) { pos = NULL; delete pos; break; } pos = pos->next; val = pos->data; } cout << endl; } | Crocus |
2-1 위의 코드 일부분 중 node *pos = head;를 한 이유가 무엇일까?
한번도 생각해보지 않고 넘어간다면 나중에 직접 코드를 구현 해 볼때 왜 원하는 대로 값이 안나오는지 겪어 볼 수 있다.
이 코드를 구현한 이유는 현재 head->see(head);를 이용하여
head가 가리키는 노드(노드를 클래스로 만들었음을 인지하자)의 see 함수를 통해 head를 인자로 가져왔다.
즉, 어떤 말을 하고 싶은 것이냐면 node *pos = head;를 하지 않고 head = head->next; 같은 방식으로 head를 조작하게 되면
head가 처음을 가리키지 못하고 다른곳으로 가버린다.
따라서 head가 가리키는 위치를 복사하는 pos 포인터를 이용하여 pos가 움직이게 하도록 한다.
2-2 위의 코드 일부분 중 pos = NULL;을 한 이유가 무엇일까?
pos = NULL;을 하지 않는다면 나타나는 결과는 아래와 같다.
4가 사라졌음을 볼 수 있다. 왜 사라졌을까??
delete pos;를 유심히 보면, 이 코드는 pos가 가리키는 곳을 해제 시킨다는 뜻을 의미한다.
지금 만들어진 노드들은 어느 포인터에 의해서도 그 포인터가 노드를 가리키게 된다면 메모리 해제를 할 수 있게 된다.
바보같아 보이는 코드이지만 이 코드는 이 부분에 대해 조금 더 경각심을 가지게 하기 위한 코드이고
누군가에게 즉흥적으로 물었을 때, 자료구조 및 포인터가 어설프게 확립된 코더라면 충분히 햇갈릴 소지가 있다고 보기에 작성하였다.
사실은 pos = NULL; delete pos;를 지우고 break;만 두면 정상동작한다.
포인터는 일반 변수와 달리 주소를 가지고 행동하기에 아주 정교하고 조심스럽게 생각 해 볼 필요가 있다.
'Applied > 자료구조' 카테고리의 다른 글
원형 연결 리스트(Cricular Linked List) (0) | 2016.05.04 |
---|---|
연결 리스트(Linked List) 개념 (4) - 최종 코드 및 과정 수록 (0) | 2016.05.04 |
연결 리스트(Linked List) 개념 (3) (0) | 2016.05.03 |
연결 리스트(Linked List) 개념 (2) (0) | 2016.05.03 |
연결 리스트(Linked List) 개념 (1) (0) | 2016.05.03 |