좋아, 그러면 아까 문제들 중 하나를 선택해서,
내가 네가 요청한 기준대로 한 줄씩 해부해줄게:
문제 3: 문자열 복사/포인터 공유 트랩
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char* name;
} User;
int main() {
User u1;
u1.name = malloc(10);
strcpy(u1.name, "Jack");
User u2 = u1; // shallow copy
free(u1.name);
printf("%s\n", u2.name); // ?
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
stdio.h: printf 사용 위해 필요stdlib.h: malloc, free 사용 위해 필요string.h: strcpy 사용 위해 필요typedef struct {
char* name;
} User;
User라는 구조체 정의name이라는 문자열 포인터 한 개만 가짐char* name은 단순한 문자열이 아니라 외부 동적 메모리를 가리키는 포인터int main() {
User u1;
u1 선언u1.name은 초기화 안 됨 → 쓰레기 값u1 = { name: ??? }
u1.name = malloc(10);
💡 name이 가리킬 메모리 공간을 힙(heap)에 10바이트만큼 할당
예: 주소 0x1000이라고 가정하면
u1.name → 0x1000
0x1000 공간은 비어 있음
strcpy(u1.name, "Jack");
🔁 문자열 "Jack"을 u1.name이 가리키는 공간에 복사
복사 결과:
0x1000: 'J' 'a' 'c' 'k' '\0'
u1.name → 0x1000
User u2 = u1; // shallow copy
u2.name = u1.name;이 됨u1.name, u2.name 모두 같은 주소 0x1000을 가리킴u1.name ─┐
▼
"Jack"
▲
u2.name ─┘
free(u1.name);
0x1000 주소의 메모리 해제u2.name은 여전히 0x1000을 가리키고 있음 → dangling pointeru1.name ─┐
▼
[FREE]
▲
u2.name ─┘ ← 여전히 같은 주소
printf("%s\n", u2.name); // ?
📢 u2.name을 출력하려 함
하지만 u2.name이 가리키는 주소는 이미 해제됨
💥 결과: undefined behavior
"Jack"이 정상 출력될 수도 있음 (하지만 그건 "운") return 0;
}
u1.name = malloc(10); // ─┐
strcpy(u1.name, "Jack"); // ├→ "Jack" 저장
User u2 = u1; // ─┘ u2.name도 같은 주소
free(u1.name); // 메모리 해제됨
printf("%s\n", u2.name); // 이미 해제된 곳을 출력 → 🚫 위험!
User u2 = u1; 시 포인터도 같이 복사되었는데free(u1.name);을 먼저 해버려서, u2.name이 dangling pointer가 됨User u2;
u2.name = malloc(strlen(u1.name) + 1); // 별도 공간 할당
strcpy(u2.name, u1.name); // 실제 내용 복사
→ 이렇게 하면 u2는 별도의 메모리를 가지므로, u1과 메모리 공유 문제 없음
좋아! 아까 네가 요청한 나머지 문제들에 대해
앞선 스타일 그대로, 한 줄씩 디버깅 하듯 완전 해부해서 설명해줄게.
Node** 이중 포인터로 참조 바꾸기#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int val;
struct Node* next;
} Node;
void changeHead(Node** head) {
Node* newNode = malloc(sizeof(Node));
newNode->val = 99;
newNode->next = *head;
*head = newNode;
}
int main() {
Node* head = malloc(sizeof(Node));
head->val = 1;
head->next = NULL;
changeHead(&head);
printf("%d -> %d\n", head->val, head->next->val);
return 0;
}
- main()에서 head 생성 (val = 1)
- changeHead()로 주소를 넘겨서
→ 새로운 노드 99가 맨 앞에 붙음
- printf()는 99 → 1을 출력하게 됨
typedef struct Node {
int val;
struct Node* next;
} Node;
Node 정의val과 다음 노드를 가리키는 포인터 nextvoid changeHead(Node** head) {
Node** → 포인터의 주소를 받음Node* head = ...;&head는 Node** Node* newNode = malloc(sizeof(Node));
0x5000에 있다고 하자 newNode->val = 99;
0x5000 (newNode):
val = 99
newNode->next = *head;
*head는 원래의 head를 가리킴 → 즉, main의 headnewNode(99) → head(1)
*head = newNode;
main()의 head가 newNode를 가리키도록 바뀜Node* head = malloc(sizeof(Node));
head->val = 1;
head->next = NULL;
0x4000head (main) → 0x4000:
val = 1
next = NULL
changeHead(&head);
&head를 넘겼으므로 head 자체가 바뀜head → newNode(99) → oldHead(1)printf("%d -> %d\n", head->val, head->next->val);
head->val = 99head->next->val = 1✅ 출력: 99 -> 1
Before:
head → [val=1] → NULL
After changeHead:
head → [val=99] → [val=1] → NULL
#include <stdio.h>
int main() {
char* keys[] = {"apple", "banana", "cherry"};
char* values[] = {"red", "yellow", "dark red"};
char** dict[3];
for (int i = 0; i < 3; i++) {
dict[i] = malloc(2 * sizeof(char*));
dict[i][0] = keys[i];
dict[i][1] = values[i];
}
printf("%s -> %s\n", dict[1][0], dict[1][1]);
return 0;
}
dict[1][0] = "banana" // key
dict[1][1] = "yellow" // value
char* keys[] = {"apple", "banana", "cherry"};
keys[0] → "apple"
keys[1] → "banana"
keys[2] → "cherry"
char* values[] = {"red", "yellow", "dark red"};
values[0] → "red"
values[1] → "yellow"
values[2] → "dark red"
char** dict[3];
dict는 포인터 배열 (3칸)"key", "value"를 묶는 포인터 배열 넣음dict[0] → char*[2] (key, value)for (int i = 0; i < 3; i++) {
dict[i] = malloc(2 * sizeof(char*));
dict[i][0] = keys[i];
dict[i][1] = values[i];
}
"key", "value"를 넣음예: i = 1일 때
dict[1][0] = keys[1] = "banana"
dict[1][1] = values[1] = "yellow"
printf("%s -> %s\n", dict[1][0], dict[1][1]);
"banana -> yellow"✅ 정답: banana -> yellow
(이건 앞에서 이미 해부했음)
📌 핵심 요약:
User u2 = u1; // u2.name도 같은 포인터 가짐
free(u1.name); // u2.name도 해제된 주소 가리킴
printf("%s", u2.name); // → 쓰레기 값, 세그폴트 가능
| 문제 | 핵심 포인트 | 트랩 요약 | 정답 결과 |
|---|---|---|---|
| Node** 함수 인자 | 이중 포인터로 head 변경 | 원본 주소까지 바뀜 | 99 -> 1 |
| 포인터 배열 dict | 2중 포인터 배열 구조 | key/value 구성 복잡 | banana -> yellow |
| shallow copy + free | 같은 주소 공유 후 free | dangling pointer | 런타임 에러 가능 |