반응형

자료구조 코딩 시 주의사항




이전 게시물에서 그대로 가져온 [ 클래스를 통한 표현 ]의 코드이다.


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;만 두면 정상동작한다.




포인터는 일반 변수와 달리 주소를 가지고 행동기에 아주 정교하고 조심스럽게 생각 해 볼 필요가 있다.




반응형