맞아! 구조체(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 → &ba.next->next → b.next → &ca.next->next->value → c.value → 30✅ 즉, a가 b를 가리키고, b가 c를 가리키니, 결국 a를 통해 c의 값을 가져온 것!
a.next->value = 100;
이건 뭐야?
a.next → ba.next->value는 결국 b.value를 의미해.그럼 구조는 이렇게 변함:
a (value=10) -> b (value=100) -> c (value=30)
어렵게 느껴지는 건, 포인터가 여러 겹이고, 그 안에 또 포인터가 있어서인데, 한 단계씩 따라가야 해.
Node* head = &a;
head->next->next->value = 999;
head → &ahead->next → &bhead->next->next → &chead->next->next->value → c.value = 999즉, 연결된 구조를 따라가면서 최종 노드에 도달해서 값을 바꾼 것.
괄호로 구분해서 읽기: (((head)->next)->next)->value
변수 이름은 그림 그리듯 시각화하기:
head → a → b → c디버깅 중간중간 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 |
|---|---|---|
| a | 1 | \&b |
| b | 2 | \&c |
| c | 3 | \&d |
| d | 4 | NULL |
Node* p = &a;p는 a를 가리킨다.p == &aNode* q = a.next;a.next는 &b였으므로q == &bp->next->value = 10;p == &a, 그래서 p->next == a.next == &bp->next->value == b.valueb.value = 10; 로 바뀜→ b의 값이 2 → 10 으로 변경
| 노드 | value | next |
|---|---|---|
| a | 1 | \&b |
| b | 10 | \&c |
| c | 3 | \&d |
| d | 4 | NULL |
q->next->next->value = 20;q == &b, 따라서q->next == &cq->next->next == c.next == &dq->next->next->value == d.value→ 🔧 d.value = 20; 로 바뀜
| 노드 | value | next |
|---|---|---|
| a | 1 | \&b |
| b | 10 | \&c |
| c | 3 | \&d |
| d | 20 | NULL |
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);하나씩 살펴보자:
a.value→ 1 (초기 설정값에서 바뀐 적 없음)
b.next->valueb.next == &cb.next->value == c.value == 3→ 값: 3
d.value20→ 값: 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;
}
a (value=1) -> b (value=2) -> c (value=3) -> NULL
a.next = &bb.next = &cc.next = NULLp = &aq = p->next = a.next = &bq->value = 10;q는 &b를 가리킴q->value == b.valueb.value = 10;변경 후 상태
a (1) -> b (10) -> c (3)
p->next = q->next;p = &aq = &bq->next = &cp->next = &c즉, a.next가 &c로 변경됨
👉 b가 리스트에서 "끊어진 것처럼" 된다.
변경 후 연결 상태
a (1) --> c (3)
b (10) -> c (3) (이 연결은 남아있지만, a에서는 b를 건너뜀)
p->next->value = 20;p = &ap->next = &cp->next->value = c.value = 20c의 값이 3 → 20 으로 바뀜
q->next = &a;q = &bq->next = &ab.next는 a를 가리킴 → 순환 구조 발생b (10) → a (1) → c (20)
하지만 이건 출력에 영향 없음, 왜냐면 순환 구조는 printf()에서 안 써.
| 변수 | value | next |
|---|---|---|
| a | 1 | \&c (→ c.value = 20) |
| b | 10 | \&a |
| c | 20 | NULL |
printf("%d %d %d\n", a.value, b.value, c.value);
값만 따지면:
a.value → 1b.value → 10c.value → 201 10 20
최종 연결 구조는:
a (1) → c (20)
↑
└── b (10)
↓
a (1) ... (순환)
(출력은 순환과 무관하게 value만 가져오므로 순환 구조는 영향 없음)