📝 2025 C언어 특강 내용 기록
😼 Github : https://github.com/mad-cost/C-structures-basics
Day 1, Day 2에서는
배열을 이용해 Stack과 ArrayList를 직접 구현해 보았습니다.
배열 기반 구조의 가장 큰 문제점은
데이터를 중간에 삽입하거나 삭제할 때마다
인덱스를 계속 신경 써야 한다는 것❗
결정적으로 배열은
크기가 고정된 자료구조이기 때문에,
처음에 정한 크기만큼만
데이터를 저장할 수 있다는 한계도 있습니다.
그래서 이번 Day 3에서는
배열을 과감하게 버리고,
주소로 연결된 구조,
즉 Linked List를 구현해 보았습니다.
💡 Linked List를 구현하며 느낀 핵심
배열이
인덱스를 기준으로 접근하는 구조라면,
Linked List는
데이터의 위치가 아니라,
각 노드의 연결을 생각하는 구조라는 것❗
즉, 이제부터는
지금까지 우리가 공부하고 연마해 온
포인터가 갑자기 의미를 갖기 시작하는 순간이 온 것입니다 ✨
🎨 LinkedList 그림으로 이해하기
LinkedList를 직접 구현한 코드는
이미 Github에 정리해 두었으니,
여기서는 블그의 특성을 살려서
코드적게 그림은 많게,
최대한 그림을 통해 직관적으로
LinkedList에서의
데이터저장과 출력에 대하 알아보도록 합시다 ☺️
🧱 구조체는 알고가자
typedef struct node
{
struct node *nowAdd;
int data;
struct node *nextAdd;
} node;
코드를 보는 순간
벌써부터 머리가 지끈거리기 시작합니다 😓
하지만 하나씩 뜯어보면 생각보다 단순합니다.
구조체를 이용해 만들어진 하나의 데이터 묶음을
노드(Node) 라고 부릅니다.
LinkedList는
이 노드들이 주소로 서로 연결되어 있는 구조입니다.
선언된 노드의 멤버에 대한 역할
- nowAdd : 자기 자신의 주소
- data : 사용자가 입력한 값을 실제 저장할 변수
- nextAdd : 다음 노드를 가리키는 주소
📌 중요한 포인트
Linked List에서 정말 중요한 건
data보다도 다음 노드를 가리키는 주소 nextAdd 입니다.
LinkedList는 주소를 따라 이동하며 다음 데이터를 찾기 때문입니다.
🧩 데이터 삽입 그림으로 이해하기

1️⃣ Head 노드 생성
LinkedList의 시작점은 항상 head 노드입니다.
프로그램이 시작되면
malloc을 사용해 head 노드를 동적으로 할당합니다.
아직 사용자가 입력된 데이터가 없기 때문에
head 노드는 다음 노드를 가리키지 못하는 상태이며,
따라서 다음 노드를 리키는
nextAdd에는 NULL을 넣어줍니다.
📌 여기서 0x001은 실제 주소가 아닌 이해를 돕기 위한 예시 주소입니다.
💡 head를 포인터로 사용하는 이유
Github 코드에서는 head를 구조체 형태로 만들었지만,
head의 본질적인 역할은 "첫 번째 노드를 가리키는 것" 입니다.
따라서
- 구조체로 만들어도 되고
- 포인터 하나로 가볍게 구현하는 것도 좋은 방법같습니다.

2️⃣ temp 포인터 생성
이제 데이터를 삽입하거나 탐색하기 위해
temp 포인터를 하나 생성합니다.
node *temp = head;
즉,
temp는 head와 같은 주소를 가리키며 시작합니다.
📌첫 번째 노드부터 탐색하기 위해
❓ temp 포인터가 왜 필요할까
만약 temp 노드 없이
head 포인터를 직접 이동시키며 탐색한다면,
head가 가리키는 위치가 계속 바뀌고
결국 LinkedList는 시작점 자체를 잃어버리게 됩니다 😱
즉,
temp 포인터의 역할은
head는 그대로 유지한 채
노드를 하나씩 순회하며 현재 노드의 주소를 임시로 저장하는 것입니다.
temp는 노드를 순회하며 현재를 탐색하는 포인터
head는 변하면 안 되는 List의 기준점

3️⃣ 사용자가 데이터를 입력

사용자가 "저장할 데이터: 1"을 입력하면
이제 실제 데이터를 저장할 노드가 동적으로 하나 생성됩니다.
아직 다음 노드가 없으므로 nextAdd는 NULL로 설정해 줍니다.

4️⃣ 첫 번째 노드 연결하기
이제 본격적으로 연결 이 일어나는 순간입니다.
temp가 위치한 노드의 다음 주소를 가리키는
nextAdd의 값이 NULL이면
temp->nextAdd = newNode;
head의 nextAdd가
newNode의 주소를 가리키게 만들어 줍니다.
📌 중요한 포인트
현재 temp는 head와 같은 노드를 가리키고 있기 때문에,
아래 두 코드는 동일한 의미를 가집니다.
temp->nextAdd = newNode,
head->nextAdd = newNode

5️⃣ 두 번째 노드 생성
사용자가 "저장할 데이터: 3"을 입력하면
실제 데이터를 저장할 노드가 동적으로 하나 생성됩니다.
아직 다음 노드가 없으므로 nextAdd는 NULL로 설정해 줍니다.
(6번에 해당하는 그림은 아래)
6️⃣ temp가 다음 노드로 이동하는 조건
두 번째 노드를 연결하기 위해서는
어디에 삽입해야 하는지 먼저 판단해야 합니다.
이를 위해 아래와 같은 while문을 사용합니다.
while (temp->nextAdd != NULL && temp->nextAdd->data < saveNum) {
temp = temp->nextAdd;
}
while문의 첫 번째 조건
temp → nextAdd != NULL
head는 다음 노드와 연결 되어 있고,
head의 nextAdd는 0x111
즉, 다음 노드가 존재하므로
✅ true
while문의 두 번째 조건
temp → nextAdd->data < saveNum
temp → nextAdd → data 의 값은: 1
saveNum의 값은: 3
1 < 3 이므로
✅ true
두 조건이 모두 true이기 때문에
while문은 정상적으로 실행됩니다.
temp = temp->nextAdd;
이제 temp는 기존에 가리키던 head가 아니라
다음 노드(주소: 0x111) 를 가리키게 됩니다.

(7번에 해당하는 그림은 아래)
7️⃣ 두 번째 노드 연결하기
두 번째 노드를 연결한다는 것은,
현재 List에 저장된 값들보다
더 큰 값이 입력되었을 때
해당 노드를 List의 맨 뒤에 붙이는 것과 같은 의미입니다.
🔚 while문이 종료되는 순간의 의미
현재 temp는
주소 0x111에 위치한 노드를 가리키고 있습니다.
이 상태에서 while문의 조건은
false가 됩니다.
while (temp->nextAdd != NULL && temp->nextAdd->data < saveNum) {
temp = temp->nextAdd;
}
첫 번째 조건이 false인 경우
temp가 List로 연결된 노드 중 가장 마지막 노드에 위치해 있다는 의미입니다.
두 번째 조건이 false인 경우
List를 순회하는 동안 현재까지 만난 모든 노드의 data 값이 저장할 데이터보다 작았다는 의미입니다.
즉, while문이 종료되었다는 것은,
저장할 데이터가 현재 List에 연결된 모든 노드의 값보다 큰 값이라는 의미가 됩니다.
🔗 마지막 노드에 newNode 연결하기
이런 경우
List의 맨 뒤에 노드를 추가하면 됩니다.
temp->nextAdd = newNode;
이 코드로 인해,
마지막 노드에 위치한 temp의 nextAdd가
newNode의 주소를 가리키게 됩니다.

8️⃣ List 중간에 newNode 삽입하기
사용자가 "저장할 데이터: 2"을 입력하면
새로운 newNode가 생성됩니다.

🔁 while문에 의해 temp가 다음 노드로 이동

while문 두 번째 조건 false 발생
❌ while문이 false일 경우 실행하는 코드
while문이 종료되었다는 것은
newNode가 들어갈 위치를 찾았다는 의미입니다.
newNode->nextAdd = temp->nextAdd;
temp->nextAdd = newNode;
이제 노드를 중간에 끼워 넣는 작업을 진행합니다.
🎯 이 두 줄은 순서가 매우 중요합니다.
newNode → nextAdd = temp → nextAdd;
해당 코드는
temp의 다음 노드였던 주소 0x333을
newNode의 nextAdd가 먼저 가리키게 합니다.

temp → nextAdd = newNode;
해당 코드는
temp의 다음 노드를 나타내는 nextAdd가
newNode를 가리키게 합니다.

LinkedList에 데이터를 저장할 때
입력된 값을 기준으로 위치를 탐색하여
자동으로 정렬된 상태를 유지하도록 구현을 완료 🌟
🖼️ 출력 그림으로 이해하기
저장한 데이터를 출력하는 과정 또한
그림으로 하나씩 따라가며 이해해 보도록 합시다.
거의 다 끝났다!!
조금만 더 힘내보도록 하자 🔥 🔥 🔥
📋 출력 형식 요구사항
노드들은 다음과 같이 연결되어 출력되어야 합니다.
(현재 노드 주소) 1 (다음 노드 주소) - (현재 노드 주소) 2 (다음 노드 주소)
temp가 첫 번째 노드부터
각 노드를 순회하며 출력

🔁 temp를 다음 노드로 이동

✔️ 출력: (0x111) 1 (0x222)
🔁 temp를 다음 노드로 이동

✔️ 출력: (0x111) 1 (0x222) - (0x222) 2 (0x333)
🔁 temp를 다음 노드로 이동

✔️ 출력: (0x111) 1 (0x222) - (0x222) 2 (0x333) - (0x333) 3 (00000000)
📚 다음 시간까지
삭제 기능 구현
- 삭제할 데이터를 입력받는다.
- 해당 노드를 List에서 제거한다.
- 삭제된 노드의 앞/뒤 노드가 서로 연결되도록 처리한다.
- 삭제 후에도 LinkedList의 연결 구조가 유지되어야 한다.
📌 포인트
노드를 삭제할 때도 결국 다음 주소를 나타내는 nextAdd를 어떻게 바꾸느냐가 핵심
✍️ 마무리하며
LinkedList도 이제 슬슬 마무리 단계에 접어들었다.
라이브러리 없이
밑바닥부터 LinkedList를 직접 구현해 보면서
C언어를 왜 배워야 하는지
조금은 알 것 같은 기분이 들었다.
가끔 이런 생각이 들기도 했다.
Java도 있는데
왜 대학에서는 C를 가르칠까
라이브러리를 쓰면 훨씬 편한데,
왜 사람들은
톰캣을 직접 구현해 보고
각종 자료구조를 굳이 뜯어서 만들어 볼까?
라는 의문도 있었다.
하지만 직접 구현해 보니 조금은 알 것 같았다.
겉보기에는 단순하게 동작해 보이던 자료구조들이
막상 직접 구현해 보니
생각해야 할 부분이 굉장히 많다는 것을 느꼈다.
무엇보다 LinkedList를 직접 만들어 보면서 느낀 점은,
포인터, 동적 할당, 구조체에 대한 기초가
굉장히 탄탄하게 잡힌다는 것이었다.
이번 기회를 통해
밑바닥부터 하나씩 만들어 보고,
다시 차근차근 쌓아 올리며 많은 것을 느낄 수 있었다.
읽어주셔서 감사합니다 🙇
'해피 코딩 > Algorithm' 카테고리의 다른 글
| [Day 5] 후위 표현식(Postfix) (1) | 2026.02.25 |
|---|---|
| [Day 4] Linked List 데이터의 삭제 (1) | 2026.01.16 |
| [Day 2] 자료구조의 확장 / Stack에서 ArrayList까지 (0) | 2026.01.02 |
| [Day 1] C언어의 메모리 구조 이해 / Stack구현 (1) | 2026.01.01 |
| [이론] 유니온 파인드 너무 쉬운데? (5) | 2024.12.24 |