C언어 연결 리스트 질문


(마귀사탄) #1

오늘 처음 연결리스트 부분 강의를 들었습니다.
위 소스코드는 연결리스트를 이용해서 덧셈을 수행하는 프로그램입니다.

빌드는 성공하지만 숫자 입력시 작동이 중지됩니다.
포인터들도 다 살펴봤고 연결리스트들이 이어지는 로직도 재확인했습니다.

물론 제가 잘못한게 있기때문에 오류가 나는거겠죠.
하지만 지금 제가 아무리 찾아봐도 대체 어디가 잘못된건지 잘 못찾겠어서 도움을 요청합니다…ㅠㅠ

디버깅을 하면 53번라인의 (pp_tail) -> p_next = (NODE)malloc(sizeof(NODE));부분에서 액세스 위반이 일어났다고 메시지가 뜨는데 공부를 시작한지 아직 한달도 안된 뉴비라 무슨말인지도 모르겠습니다…

동적할당에 관련된 오류가 아닐까 지레짐작은 하고있습니다만…어디가 잘못됬는지 못 짚고있어요…


(ㄴㅂㄷㄱㄷㄱ) #2

작아서 잘 안 보여요 ㅠㅜ
코드 복붙하시거나
github 이나 ldeone 같은 곳에 올려서 링크 다시는 걸 추천


(프로책팔이) #3

프로그래머들이 선호하는 코드 공유 방식:

1순위. Github, bitbucket 등 전문 코드 공유 웹사이트
2순위. 구글 드라이브

기타:

그냥 코드 올리거나 아니면 HTML에 심기


(프로책팔이) #5

수정해 드리자면
</> 라고 되어있는 걸 사용하시면 됩니다. (Ctrl + shift + C)
혹은
‘’’
‘’’
이렇게요.


(마귀사탄) #6
#include<stdio.h>
#include<malloc.h>

void AddNumber();

typedef struct
{
	int number;
	struct NODE *p_next;
} NODE;

int main()
{

	printf("연결 리스트를 공부해보자!");
	NODE *pp_head = NULL;
	NODE *pp_tail = NULL;
	NODE *p;
	int sum;

	while (1)
	{
		int data;
		printf("\n\n숫자를 입력하세요 (9999를 누르면 종료): ");
		scanf("%d", &data);

		
		if (data == 9999)
			return 0;
			
		AddNumber(&pp_head, &pp_tail,  data);
	}

	p = pp_head;
	
	while (p != NULL)
	{
		if (p != pp_head)
		{
			printf("+");
		}
		printf("%d", p->number);
		sum = sum + p->number;
		p = p->p_next;
	}
	printf(" = %d\n", &sum);

	while (pp_head != NULL)
	{
		p = pp_head;
		pp_head = pp_head->p_next;
		free(p);
	}
	
}

void AddNumber(NODE **pp_head, NODE **pp_tail, int data)
{

	if (pp_head == NULL)
	{
		(*pp_head) = (NODE*)malloc(sizeof(NODE));
		(*pp_tail) = (*pp_head);
	}

	else
	{
		(*pp_tail) -> p_next = (NODE*)malloc(sizeof(NODE));
		(*pp_tail) = (*pp_tail)->p_next;
	}

	(*pp_tail)->number = data;
	(*pp_tail)->p_next = NULL;
}


네 감사합니닷…ㅠ


(마귀사탄) #8

아 찾았습니다 AddNum함수에서 오타났네요

이게 아니라

if(*pp_head == NULL)

이렇게 해야했네요… *을 빼먹ㄷㅇ…


(프로책팔이) #9

자눼는 거짓말을 해찌


(마귀사탄) #10

…졸…려…서…잘…못…본…죄송합니더ㅓㅓㅓ


(P.노우렛지Δ) #11

왜 이중포인터를 넘기셨나요?


(ㄴㅂㄷㄱㄷㄱ) #12

저거 고쳐도 여전히 엄청난 워닝이 뜨네요…


(마귀사탄) #13

메인 함수에서 전달된 1차원 포인터의 주소값을 받아서 사용하므로 2차원 포인터로 했습니다


(마귀사탄) #15

아 이거 댓글 수정 어떻게하는거지…
아무튼 pp_head, pp_tail 포인터 변수의 주소값을 수정한다는 의미엿ㅇ습니다.
1차원 포인터의 주소값을 수정해야하므로 2차원 포인터로 처리하겠다는 생각이었슴다


(ㄴㅂㄷㄱㄷㄱ) #16

그건 나름대로 잘 생각하셧는데,
pp_head 는 pointer to pointer to Node 인 만큼

NODE **pp_head = NULL 로 선언해야 하지 않을까요?
NODE **pp_tail = NULL 도 마찬가지


(마귀사탄) #17

으…; 제가 지금 생각이 막 꼬이기시작하네욧…,.,., 포인터가 서로 누굴 가리키는지 그려보면,

(AddNumber함수)[2차원]pp_head → (메인함수)[1차원] pp_head → 할당된 노드 주소

이렇게 가리킨 상황아닌가요???
그렇다면 NODE **pp_head = NULL로 선언하는거나 NODE *pp_head=NULL로 선언하는거나 크게 차이는 없지 않나요??? 결국 둘다 노드를 가리키는거…아닌가요


(마귀사탄) #18

제가 짤땐 저렇게 생각하고 짜서… 별 상관없다고 생각햇거든요


(ㄴㅂㄷㄱㄷㄱ) #19

엄청난 차이가 있을 거 같은데요… 애초에 pp 라는 걸 붙이셨으면 ** 라고 하셧어야… 아니면 phead 라고 하셔야…


(프로책팔이) #20

따블 포인타!!
아조씨 따블


(ㄴㅂㄷㄱㄷㄱ) #21
// https://www.codentalks.com/t/topic/3095/6
// partially modified by nbdgdg (incomplete)

#include<stdio.h>
#include<stdlib.h>


typedef struct node_struct
{
    int number;
    struct node_struct *p_next;
} NODE;

void AddNumber(NODE **pp_head, NODE **pp_tail, int data)
{

    if (*pp_head == NULL)
    {
        (*pp_head) = (NODE*)malloc(sizeof(NODE));
        *pp_tail = *pp_head;
        printf("first node added\n");
    }

    else
    {
        (*pp_tail) -> p_next = (NODE*)malloc(sizeof(NODE));
        (*pp_tail) = (*pp_tail)->p_next;
        printf("last node added\n");
    }

    (*pp_tail)->number = data;
    (*pp_tail)->p_next = NULL;

    return ;
}

int main()
{

    printf("연결 리스트를 공부해보자!");
    NODE *p_head = NULL;
    NODE *p_tail = NULL;
    NODE **pp_head = &p_head;
    NODE **pp_tail = &p_tail;
    int sum = 0;

    while (1)
    {
        int data;
        printf("\n\n숫자를 입력하세요 (9999를 누르면 종료): ");
        scanf("%d", &data);


        if (data == 9999){
            break;
        }

        AddNumber(pp_head, pp_tail,  data);
    }

    printf("Linked List: ");
    while (p_head != NULL)
    {
        printf("%d -> ", p_head->number);
        sum = sum + p_head->number;
        p_head = p_head->p_next;
    }

    printf("NULL\nsum = %d\n", sum);

    while (pp_head != NULL)
    {
        p_head = *pp_head;
        *pp_head = p_head->p_next;
        free(p_head);
    }

    return 0;

}

일단 대충 오류, 워닝없게 고쳐보앗음니다.
원하시는 대로 작동하는지?

Linked List:

head    tail
O O O O O 
^       ^
|       |
p_head   p_tail
^         ^
|         |
pp_head    pp_tail 

당연한 거지만 pp_head 쓰지 않고 &p_head 로 인자로 넘겨도 됩니다. 위 코드에서 하신 것 처럼요.
이해를 돕기 위해 explicit 하게 명시함.


(마귀사탄) #22

소름…

감사합니다 참고해서 열심히 공부하겟슴다 ㅠㅠㅠ


(프로책팔이) #24

코알못이라 그런데

while (pp_head != NULL)

while (p_head != NULL && pp_head != NULL)

로 바꿔야 되는 건 아닙니까?

수정된 코드요.
// https://www.codentalks.com/t/topic/3095/6
// partially modified by nbdgdg (incomplete)

// partially modified by withdraw (later)

#include<stdio.h>
#include<stdlib.h>


typedef struct node_struct
{
    int number;
    struct node_struct *p_next;
} NODE;

void AddNumber(NODE **pp_head, NODE **pp_tail, int data)
{

    if (*pp_head == NULL)
    {
        (*pp_head) = (NODE*)malloc(sizeof(NODE));
        *pp_tail = *pp_head;
        printf("first node added\n");
    }
   else
    {
        (*pp_tail) -> p_next = (NODE*)malloc(sizeof(NODE));
        (*pp_tail) = (*pp_tail)->p_next;
        printf("last node added\n");
    }

    (*pp_tail)->number = data;
    (*pp_tail)->p_next = NULL;

    return ;
}

int main()
{

    printf("연결 리스트를 공부해보자!");
    NODE *p_head = NULL;
    NODE *p_tail = NULL;
    NODE **pp_head = &p_head;
    NODE **pp_tail = &p_tail;
    int sum = 0;

    while (1)
    {
        int data;
        printf("\n\n숫자를 입력하세요 (9999를 누르면 종료): ");
        scanf("%d", &data);


        if (data == 9999){
            break;
        }

        AddNumber(pp_head, pp_tail,  data);
    }

    printf("Linked List: ");
    while (p_head != NULL)
    {
        printf("%d -> ", p_head->number);
        sum = sum + p_head->number;
        p_head = p_head->p_next;
    }

    printf("NULL\nsum = %d\n", sum);

//여기가 바뀜
while (p_head != NULL && pp_head != NULL)
    {
        p_head = *pp_head;
        *pp_head = p_head->p_next;
        free(p_head);
    }

    return 0;

}