구조체 연습 2(주의)

류한선·2025년 7월 4일

실기연습-2

목록 보기
84/95

좋아, 네가 요청한 3가지 주제를 바탕으로 각각 헷갈리기 쉬운 계산 흐름 구조체 문제를 하나씩 만들어볼게. 숫자는 많지 않지만, 정확한 흐름 추적이 핵심이야.


✅ 문제 1. 구조체 배열에서 한 원소만 수정 흐름 추적 (체크)

#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
④ 컴파일 에러


✅ 해설:

  • 구조체 배열 복사는 멤버 단위 복사 (deep copy 아님이지만, 값형이라 안전)
  • 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}

정답: ①


✅ 문제 2. 포인터가 여러 구조체를 가리킬 때 수정 추적 (체크)

#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 = &b
  • p = q; → 이제 pb를 가리킴
  • 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

정답: ①


✅ 문제 3. 구조체 내 배열 vs 포인터 혼동 트랩 (체크)

#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가지 주제에 맞춘 실전 트랩 문제 + 해설을 줄게. 각각은 함수 호출/메모리/복사 흐름에서 실수가 발생하기 쉬운 코드로 구성되어 있어.


✅ 문제 1: 구조체 배열을 함수 인자로 넘겼을 때 전체 값 vs 개별 값 처리

#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

✅ 정답: ②


✅ 문제 2: 구조체 내 포인터 멤버 deep copy vs shallow copy (체크)

#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);

✅ 문제 3: 이중 포인터 + 구조체 배열 혼합 트랩 (체크)

#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 복사
2deep vs shallow copy포인터 멤버 공유
3이중 포인터 혼동같은 주소 공유

좋아! 이제 네가 요청한 주제들로 실전 스타일 구조체 문제 3개를 준비했어.
이 문제들은 얕은 복사 후 정렬 오류, qsort에서 void 처리 실수*, 함수 포인터 배열 혼동**을 유도하는 실무급 트랩 문제야.


✅ 문제 1: 구조체 배열 정렬 후 shallow copy로 인한 오류

#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 중복 오류


✅ 문제 2: void* 기반 qsort 비교 함수 실수 (체크)

#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


✅ 해설:

  • arrStudent 배열이므로 void* aStudent*로 바로 변환 가능해야 함
  • 그런데 이 코드는 Student**로 잘못 변환 → 실제 aStudent인데 두 번 역참조
  • 결과적으로 잘못된 메모리 접근 → Segfault 가능

정답: ④ 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;
}

✅ 문제 3: 구조체 내 함수 포인터 배열 트랩

#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]은 여전히 NULL
  • o.ops[1](); → NULL 함수 포인터 호출 → Segfault

정답: ② Hello (이후 Segfault)


🧠 요약

문제주제트랩 포인트정답
1구조체 shallow copyqsort 후 double free
2void* 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;
}

❓ 각각 출력되는 값은?

  1. head->next->next->key → ?
  2. head->next->key → ?
  3. head->key → ?
  4. head->next->next->next → ?

✅ 해설 (한 줄씩 추적)

구조체 구성:

head
 ├─ key = "A"
 ├─ value = "1"
 └─ next →
         ├─ key = "B"
         ├─ value = "2"
         └─ next →
                 ├─ key = "C"
                 ├─ value = "3"
                 └─ next = ??? (초기화 안됨 → 쓰레기 or NULL)

📌 각 출력 분석

1. head->next->next->key

  • headnextnextkey = "C"
    ✅ 정답: "C"

2. head->next->key

  • headnextkey = "B"
    ✅ 정답: "B"

3. head->key

✅ 정답: "A"

4. head->next->next->next

  • 마지막 노드의 next초기화되지 않음
  • 이건 쓰레기값 or NULL 아님
    출력 시 Segmentation Fault 가능성 매우 높음

✅ 예상 출력 (환경 따라 달라짐)

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가지 고급 트랩 주제로 헷갈리기 쉬운 구조체 문제들을 각각 준비했어.
모두 실제 시험이나 실무에서도 사고를 유도하기 쉬운 코드 흐름이야.


✅ 문제 1: 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;
}

❓ 출력 결과는?

1 -> 1
99 -> 1
1 -> 99
④ 컴파일 에러


✅ 해설:

  • changeHead(&head) → 이중 포인터로 원본 head 수정
  • 새 노드(99)를 만들고 → head 앞에 붙임
newNode(99) → head(1) → NULL

newNode ──► [val: 99 | next: ──► [val: 1 | next: NULL]]

  • head->val == 99
  • head->next->val == 1

🎯 최종 출력

99 -> 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 동작 가능
  • 하지만 메모리 관리와 인덱스 오류 위험 큼 (조금만 틀려도 segfault)

✅ 문제 3: 문자열 복사 안 하고 shallow copy 오류 유도

#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.nameu2.name이 같은 포인터를 공유 (shallow copy)
  • free(u1.name); 이후 u2.name은 dangling pointer
  • printf("%s", u2.name)use-after-free

✅ 정답: ② 쓰레기 값 or 런타임 에러


🧠 요약

문제주제트랩정답
1Node** 이중 포인터함수에서 head 수정
2포인터 배열로 dict3중 포인터 흐름
3shallow copyfree 후 접근

0개의 댓글