구조체

류한선·2025년 6월 27일

실기연습-2

목록 보기
47/95

맞아! 구조체(struct)를 활용한 문제, 특히 연결리스트(linked list), 트리(tree), 그래프(graph) 등에서 -> 연산자를 사용하는 경우가 많은데, 이게 한 번만 사용하는 게 아니라 여러 번 꼬리에 꼬리를 물듯이 쓰여서 혼란스러울 수 있어. 예를 들어 이런 코드들:

node->next->next->value

이건 초보자 입장에서는 거의 악몽이야 😅


✅ 핵심 먼저 짚고 가자

  • -> 연산자는 포인터가 가리키는 구조체의 멤버에 접근할 때 사용해.
  • 구조체 포인터가 구조체를 가리키고 있고, 그 구조체 안에 다시 포인터가 있을 수 있어.

✅ 예시 구조체부터 살펴보자

typedef struct Node {
    int value;
    struct Node* next;
} Node;

이 구조체는 아주 기본적인 연결리스트 구조야.


✅ 기본적인 구조 예시

Node a, b, c;

a.value = 10;
a.next = &b;

b.value = 20;
b.next = &c;

c.value = 30;
c.next = NULL;

이제 구조는 다음과 같아:

a (value=10) -> b (value=20) -> c (value=30) -> NULL

✅ 여러 번 ->가 나오는 코드 예제

이제 여기서 이런 코드가 나왔다고 해보자:

printf("%d\n", a.next->next->value);

🔍 이게 무슨 뜻일까?

  • a.next&b
  • a.next->nextb.next&c
  • a.next->next->valuec.value30

즉, ab를 가리키고, bc를 가리키니, 결국 a를 통해 c의 값을 가져온 것!


✅ 더 복잡한 예제 (중간에서 값 바꾸기)

a.next->value = 100;

이건 뭐야?

  • a.nextb
  • a.next->value는 결국 b.value를 의미해.
  • 그래서 b의 값을 100으로 바꿈.

그럼 구조는 이렇게 변함:

a (value=10) -> b (value=100) -> c (value=30)

✅ 디버깅 시, 어떻게 따라가야 하나?

어렵게 느껴지는 건, 포인터가 여러 겹이고, 그 안에 또 포인터가 있어서인데, 한 단계씩 따라가야 해.

예시:

Node* head = &a;
head->next->next->value = 999;
  • head&a
  • head->next&b
  • head->next->next&c
  • head->next->next->valuec.value = 999

즉, 연결된 구조를 따라가면서 최종 노드에 도달해서 값을 바꾼 것.


✅ 디버깅 팁

  1. 괄호로 구분해서 읽기: (((head)->next)->next)->value

  2. 변수 이름은 그림 그리듯 시각화하기:

    • head → a → b → c
  3. 디버깅 중간중간 printf로 찍어보기:

    printf("b.value = %d\n", a.next->value);
    printf("c.value = %d\n", a.next->next->value);

좋아! 그럼 지금부터 이 C 코드의 각 줄을 디버깅하듯이 해부해서, 각 변수/포인터가 어떻게 움직이고 값이 어떻게 바뀌는지를 완전히 분석해줄게.


✅ 전체 코드 다시 보기

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

typedef struct Node {
    int value;
    struct Node* next;
} Node;

int main() {
    Node a, b, c, d;

    a.value = 1;
    b.value = 2;
    c.value = 3;
    d.value = 4;

    a.next = &b;
    b.next = &c;
    c.next = &d;
    d.next = NULL;

    Node* p = &a;
    Node* q = a.next;

    p->next->value = 10;
    q->next->next->value = 20;
    q = q->next;

    printf("%d %d %d\n", a.value, b.next->value, d.value);

    return 0;
}

📌 구조체 상태 초기화

우선 아래처럼 연결 리스트가 구성돼:

a -> b -> c -> d -> NULL

각 노드의 값 초기 상태:

노드값 (value)next
a1\&b
b2\&c
c3\&d
d4NULL

🧩 한 줄씩 해석하기


Node* p = &a;

  • 포인터 pa를 가리킨다.
  • 즉, p == &a

Node* q = a.next;

  • a.next&b였으므로
  • q == &b

p->next->value = 10;

👀 무슨 일이 벌어지는가?

  • p == &a, 그래서 p->next == a.next == &b
  • 그러면 p->next->value == b.value
  • 🔧 b.value = 10; 로 바뀜

b의 값이 2 → 10 으로 변경


현재 상태 요약:

노드valuenext
a1\&b
b10\&c
c3\&d
d4NULL

q->next->next->value = 20;

👀 무슨 일이 벌어지는가?

  • q == &b, 따라서
  • q->next == &c
  • q->next->next == c.next == &d
  • 그래서 q->next->next->value == d.value

→ 🔧 d.value = 20; 로 바뀜


현재 상태 요약:

노드valuenext
a1\&b
b10\&c
c3\&d
d20NULL

q = q->next;

  • q == &b 였는데
  • q = q->next == &c

이후부터 q는 c를 가리킨다.

하지만 이 q 변경은 이후 코드에 영향 없음
(출력에 q 사용 안 하니까 지금은 무시해도 됨)


🖨️ 마지막 줄: printf("%d %d %d\n", a.value, b.next->value, d.value);

하나씩 살펴보자:


1️⃣ a.value

1 (초기 설정값에서 바뀐 적 없음)


2️⃣ b.next->value

  • b.next == &c
  • b.next->value == c.value == 3

값: 3


3️⃣ d.value

  • 위에서 바꿨음 → 20

값: 20


✅ 최종 출력값:

1 3 20

🎉 정답: 1 3 20


📌 시각화 요약

[p] → a(value=1) → b(value=10) → c(value=3) → d(value=20)
                           ↑
                          [q] (초기: b, 나중: c)

좋아! 지금부터 아래 C 코드가 출력하는 a.value, b.value, c.value가 왜 그렇게 나오는지를 한 줄씩, 예제 값 넣어서 디버깅 수준으로 완벽히 해설해줄게.


✅ 코드 다시 보기

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

typedef struct Node {
    int value;
    struct Node* next;
} Node;

int main() {
    Node a, b, c;

    a.value = 1;
    b.value = 2;
    c.value = 3;

    a.next = &b;
    b.next = &c;
    c.next = NULL;

    Node* p = &a;
    Node* q = p->next;

    q->value = 10;
    p->next = q->next;
    p->next->value = 20;
    q->next = &a;

    printf("%d %d %d\n", a.value, b.value, c.value);
    return 0;
}

🔧 [1단계] 초기 구조 상태

구조체 연결 상태:

a (value=1) -> b (value=2) -> c (value=3) -> NULL
  • a.next = &b
  • b.next = &c
  • c.next = NULL

포인터 상황:

  • p = &a
  • q = p->next = a.next = &b

🔧 [2단계] q->value = 10;

  • q&b를 가리킴
  • q->value == b.value
  • 그래서 b.value = 10;

변경 후 상태

a (1) -> b (10) -> c (3)

🔧 [3단계] p->next = q->next;

  • p = &a
  • q = &b
  • q->next = &c
  • 그래서 p->next = &c

즉, a.next&c로 변경됨
👉 b가 리스트에서 "끊어진 것처럼" 된다.

변경 후 연결 상태

a (1) --> c (3)
b (10) -> c (3) (이 연결은 남아있지만, a에서는 b를 건너뜀)

🔧 [4단계] p->next->value = 20;

  • p = &a
  • p->next = &c
  • 그러므로 p->next->value = c.value = 20

c의 값이 3 → 20 으로 바뀜


🔧 [5단계] q->next = &a;

  • q = &b
  • q->next = &a
    → 이제 b.nexta를 가리킴 → 순환 구조 발생
b (10) → a (1) → c (20)

하지만 이건 출력에 영향 없음, 왜냐면 순환 구조는 printf()에서 안 써.


✅ 최종 변수 값 정리

변수valuenext
a1\&c (→ c.value = 20)
b10\&a
c20NULL

🖨️ 마지막 줄 출력 분석

printf("%d %d %d\n", a.value, b.value, c.value);

값만 따지면:

  • a.value1
  • b.value10
  • c.value20

✅ 정답 출력

1 10 20

🧠 시각화 요약

최종 연결 구조는:

a (1) → c (20)
↑
└── b (10)
     ↓
     a (1) ... (순환)

(출력은 순환과 무관하게 value만 가져오므로 순환 구조는 영향 없음)


0개의 댓글