좋아, 네가 요청한 3가지 주제를 바탕으로 각각 헷갈리기 쉬운 계산 흐름 구조체 문제를 하나씩 만들어볼게. 숫자는 많지 않지만, 정확한 흐름 추적이 핵심이야.
#include <stdio.h>
typedef struct {
int val;
} Item;
int main() {
Item arr[3] = {{1}, {2}, {3}};
Item copy[3];
for (int i = 0; i < 3; i++) {
copy[i] = arr[i]; // 복사
}
arr[1].val = 10;
printf("%d %d %d\n", copy[0].val, copy[1].val, copy[2].val);
return 0;
}
① 1 2 3
② 1 10 3
③ 1 0 3
④ 컴파일 에러
copy[i] = arr[i];로 초기 값 복사됨arr[1].val = 10;은 copy에 영향 없음| 구분 | 값형(Value type) | 참조형(Reference type) |
|---|---|---|
| 저장 방식 | 실제 값을 직접 저장 | 값이 있는 “주소(참조)”를 저장 |
| 복사 시 | 값을 그대로 복사 → 완전히 독립된 복사본 | 주소를 복사 → 같은 데이터를 공유 |
| 예시 | int, double, struct(기본형 멤버만 있을 때) | 배열, 포인터, 동적 메모리, 클래스 |
| 구조체 형태 | 복사 방식 | 안전 여부 | 예시 |
|---|---|---|---|
| 단순 값만 있는 구조체 | 값 복사 | ✅ 안전 (서로 독립) | Item { int val; } |
| 포인터를 포함한 구조체 | 주소 복사 | ⚠️ 위험 (서로 영향) | Item { int* val; } |
📌 복사 흐름:
초기: copy = {1, 2, 3}
수정 후: arr = {1, 10, 3}
copy = {1, 2, 3}
✅ 정답: ①
#include <stdio.h>
typedef struct {
int x;
} Point;
int main() {
Point a = {5}, b = {10};
Point* p = &a;
Point* q = &b;
p = q;
q->x = 20;
printf("%d %d\n", a.x, b.x);
return 0;
}
① 5 20
② 20 20
③ 5 10
④ 10 20
p = &a, q = &bp = q; → 이제 p도 b를 가리킴q->x = 20; → 결국 b.x = 20→ a는 아예 안 건드림
p = q; 는 포인터가 “무엇을 가리키는지”를 바꾸는 문장이지, 값 자체를 복사하는 게 아님.
즉, p가 이제 b를 가리키게 되는 것뿐이야.
p = q;
👉 "값복사"는 맞지만, 그 값은 ‘주소값’이다.
즉, 포인터가 가리키는 대상의 주소를 복사하는 것일 뿐, 구조체의 실제 데이터는 복사되지 않는다.
구조체의 내용(a.x, b.x)은 전혀 복사되지 않았음.
https://chatgpt.com/c/69085c0e-3708-8321-aaa0-1d7570a00cc3
✅ 정답: ①
#include <stdio.h>
#include <string.h>
typedef struct {
char a[10]; // 배열 멤버
char* b; // 포인터 멤버
} Str;
int main() {
Str s1 = {"abc", NULL};
Str s2 = s1;
strcpy(s1.a, "xyz");
s1.b = "hello";
printf("%s %s\n", s2.a, s2.b);
return 0;
}
① abc hello
② xyz hello
③ abc (null)
④ abc xyz
s2 = s1; → 배열 a는 내용이 복사됨, 포인터 b는 주소만 복사됨strcpy(s1.a, "xyz"); → s2.a는 영향을 받지 않음 (배열 자체 복사이므로)s1.b = "hello"; → s2.b는 여전히 NULL (포인터는 복사되었을 뿐, 변경 안 됨)이 코드에서는 포인터가 원본에 영향을 못 끼친 이유”**는
‘이미 구조체 전체가 값복사(깊은 복사 형태)’로 한 번 복제되었기 때문
https://chatgpt.com/c/69086be5-0900-8321-aaad-b92d1de48f44
📌 결과:
s2.a = "abc" // 복사된 내용 유지
s2.b = NULL // 그대로
✅ 정답: ③
| 문제 | 주제 | 핵심 트랩 | 정답 |
|---|---|---|---|
| 1 | 구조체 배열 복사 | 복사 후 수정은 영향 없음 | ① |
| 2 | 구조체 포인터 | 포인터가 어디를 가리키는지 추적 | ① |
| 3 | 배열 vs 포인터 | 배열은 복사, 포인터는 얕은 복사 | ③ |
좋아! 이번에는 네가 요청한 다음 3가지 주제에 맞춘 실전 트랩 문제 + 해설을 줄게. 각각은 함수 호출/메모리/복사 흐름에서 실수가 발생하기 쉬운 코드로 구성되어 있어.
#include <stdio.h>
typedef struct {
int val;
} Num;
void modify(Num arr[], int n) {
for (int i = 0; i < n; i++) {
arr[i].val += 10;
}
}
int main() {
Num nums[2] = {{1}, {2}};
modify(nums, 2);
printf("%d %d\n", nums[0].val, nums[1].val);
return 0;
}
① 1 2
② 11 12
③ 10 10
④ 컴파일 에러
Num arr[]는 실제로는 Num* arr → 주소 전달modify() 안에서 nums 배열의 원본에 직접 접근+10 되어 결과는 11 12✅ 정답: ②
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char* name;
} User;
int main() {
User a;
a.name = malloc(10);
strcpy(a.name, "Kim");
User b = a; // shallow copy
strcpy(b.name, "Lee");
printf("%s %s\n", a.name, b.name);
free(a.name);
return 0;
}
① Kim Lee
② Lee Lee
③ Kim Kim
④ 컴파일 에러
b = a;는 shallow copy → a.name, b.name 모두 같은 포인터strcpy(b.name, "Lee") → 결국 a.name도 "Lee"로 덮어씀✅ 정답: ②
📌 Deep Copy 해결 방법 예시:
b.name = malloc(strlen(a.name)+1);
strcpy(b.name, a.name);
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int id;
} Item;
int main() {
Item** arr = malloc(2 * sizeof(Item*));
for (int i = 0; i < 2; i++) {
arr[i] = malloc(sizeof(Item));
arr[i]->id = i + 1;
}
arr[1] = arr[0]; // 같은 곳 가리키도록 설정
arr[1]->id = 100;
printf("%d %d\n", arr[0]->id, arr[1]->id);
return 0;
}
① 1 2
② 100 100
③ 1 100
④ 컴파일 에러
arr[1] = arr[0]; → 두 포인터가 같은 메모리를 가리킴arr[1]->id = 100;은 사실상 arr[0]->id = 100;과 동일✅ 정답: ②
| 문제 | 핵심 개념 | 트랩 포인트 | 정답 |
|---|---|---|---|
| 1 | 구조체 배열 전달 | 주소 전달 vs 복사 | ② |
| 2 | deep vs shallow copy | 포인터 멤버 공유 | ② |
| 3 | 이중 포인터 혼동 | 같은 주소 공유 | ② |
좋아! 이제 네가 요청한 주제들로 실전 스타일 구조체 문제 3개를 준비했어.
이 문제들은 얕은 복사 후 정렬 오류, qsort에서 void 처리 실수*, 함수 포인터 배열 혼동**을 유도하는 실무급 트랩 문제야.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char* name;
} Person;
int compare(const void* a, const void* b) {
Person* p1 = (Person*)a;
Person* p2 = (Person*)b;
return strcmp(p1->name, p2->name);
}
int main() {
Person arr[2];
arr[0].name = malloc(10);
arr[1].name = malloc(10);
strcpy(arr[0].name, "Bob");
strcpy(arr[1].name, "Alice");
qsort(arr, 2, sizeof(Person), compare);
free(arr[0].name);
free(arr[1].name);
return 0;
}
① 아무 문제 없음
② Segmentation Fault 발생
③ 메모리 누수
④ free 중복 오류
arr[0], arr[1]은 구조체 값 배열qsort()는 구조체 전체를 값 복사하여 정렬함name만 얕게 복사됨free(arr[0].name) 이후 arr[1].name이 같은 포인터일 수 있음 → double free!✅ 정답: ④ free 중복 오류
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int score;
} Student;
int cmp(const void* a, const void* b) {
const Student* x = *(const Student**)a;
const Student* y = *(const Student**)b;
return x->score - y->score;
}
int main() {
Student arr[2] = {{80}, {90}};
qsort(arr, 2, sizeof(Student), cmp);
for (int i = 0; i < 2; i++) {
printf("%d ", arr[i].score);
}
return 0;
}
① 80 90
② 90 80
③ 컴파일 에러
④ Segmentation Fault
arr는 Student 배열이므로 void* a는 Student*로 바로 변환 가능해야 함Student**로 잘못 변환 → 실제 a는 Student인데 두 번 역참조✅ 정답: ④ Segmentation Fault
📌 수정 버전:
int cmp(const void* a, const void* b) {
const Student* x = (const Student*)a;
const Student* y = (const Student*)b;
return x->score - y->score;
}
#include <stdio.h>
typedef struct {
void (*ops[2])(void);
} Ops;
void hello() { printf("Hello "); }
void world() { printf("World\n"); }
int main() {
Ops o = {0}; // ops 배열 NULL 초기화
o.ops[0] = hello;
// o.ops[1]는 NULL
for (int i = 0; i < 2; i++) {
o.ops[i](); // 함수 호출
}
return 0;
}
① Hello World
② Hello (이후 Segfault)
③ 컴파일 에러
④ Hello NULL
o.ops[0] = hello;만 설정하고o.ops[1]은 여전히 NULLo.ops[1](); → NULL 함수 포인터 호출 → Segfault✅ 정답: ② Hello (이후 Segfault)
| 문제 | 주제 | 트랩 포인트 | 정답 |
|---|---|---|---|
| 1 | 구조체 shallow copy | qsort 후 double free | ④ |
| 2 | void* qsort 실수 | Student 아닌 Student**로 변환 | ④ |
| 3 | 함수 포인터 배열 | NULL 함수 호출 | ② |
좋아, 이번엔 key-value 구조를 가진 구조체에서 여러 번 -> 연산자를 써서
"어떤 값을 출력하는가?"를 헷갈리게 만드는 트랩 문제를 낼게.
포인터 연산과 구조체 참조가 섞이면서 초보자가 혼동하기 쉬운 유형이지.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node {
char* key;
char* value;
struct Node* next;
} Node;
int main() {
Node* head = malloc(sizeof(Node));
head->key = "A";
head->value = "1";
head->next = malloc(sizeof(Node));
head->next->key = "B";
head->next->value = "2";
head->next->next = malloc(sizeof(Node));
head->next->next->key = "C";
head->next->next->value = "3";
printf("%s\n", head->next->next->key);
printf("%s\n", head->next->key);
printf("%s\n", head->key);
printf("%s\n", head->next->next->next);
return 0;
}
head->next->next->key → ?head->next->key → ?head->key → ?head->next->next->next → ?head
├─ key = "A"
├─ value = "1"
└─ next →
├─ key = "B"
├─ value = "2"
└─ next →
├─ key = "C"
├─ value = "3"
└─ next = ??? (초기화 안됨 → 쓰레기 or NULL)
head->next->next->keyhead → next → next → key = "C""C"head->next->keyhead → next → key = "B""B"head->key✅ 정답: "A"
head->next->next->nextnext는 초기화되지 않음C
B
A
(segfault) ← 혹은 쓰레기값
✅ 주의: 마지막 출력은 undefined behavior
| 줄 | 의미 | 결과 |
|---|---|---|
head->key | 첫 노드의 key | "A" |
head->next->key | 두 번째 노드의 key | "B" |
head->next->next->key | 세 번째 노드의 key | "C" |
head->next->next->next | 초기화 안 됨 | ❌ 위험 (UB) |
좋아! 네가 요청한 3가지 고급 트랩 주제로 헷갈리기 쉬운 구조체 문제들을 각각 준비했어.
모두 실제 시험이나 실무에서도 사고를 유도하기 쉬운 코드 흐름이야.
#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;
}
① 1 -> 1
② 99 -> 1
③ 1 -> 99
④ 컴파일 에러
changeHead(&head) → 이중 포인터로 원본 head 수정head 앞에 붙임newNode(99) → head(1) → NULL
newNode ──► [val: 99 | next: ──► [val: 1 | next: NULL]]
head->val == 99 head->next->val == 199 -> 1
✅ 정답: ② `99 -> 1`
---
## ✅ 문제 2: key/value를 구조체 아닌 포인터 배열로 연결한 경우
```c
#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;
}
① banana -> yellow
② 컴파일 에러
③ 쓰레기 값
④ Segmentation Fault
dict[i]는 char** (포인터 배열)dict[1][0]은 "banana", dict[1][1]은 "yellow"가 들어 있음✅ 정답: ① banana -> yellow
✅ 포인트:
char*** 형태로 dictionary-like 동작 가능#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;
}
① Jack
② 쓰레기 값 or 런타임 에러
③ 컴파일 에러
④ (null)
u1.name과 u2.name이 같은 포인터를 공유 (shallow copy)free(u1.name); 이후 u2.name은 dangling pointerprintf("%s", u2.name) → use-after-free✅ 정답: ② 쓰레기 값 or 런타임 에러
| 문제 | 주제 | 트랩 | 정답 |
|---|---|---|---|
| 1 | Node** 이중 포인터 | 함수에서 head 수정 | ② |
| 2 | 포인터 배열로 dict | 3중 포인터 흐름 | ① |
| 3 | shallow copy | free 후 접근 | ② |