구조체 연습1(주의)

류한선·2025년 7월 4일

실기연습-2

목록 보기
83/95

좋아! 이제부터는 다음 고급 주제들에 대해 트랩 문제 + 해설을 하나씩 제공할게. 실무나 실기 시험에서 실수하기 쉬운 부분 중심으로 설명해줄게.


✅ 1. 구조체를 반환하는 함수의 위험 (복사 vs 참조) (체크)

#include <stdio.h>

typedef struct {
    int data;
} Item;

Item getItem() {
    Item temp;
    temp.data = 42;
    return temp;
}

int main() {
    Item x = getItem();
    printf("%d\n", x.data);
    return 0;
}

Q1. 출력 결과는?

① 42
② 쓰레기 값
③ 컴파일 에러
④ 런타임 에러


✅ 해설

  • 이 함수는 구조체 값 자체를 복사하여 반환
  • 로컬 변수 temp는 함수 종료 시 사라지지만, 구조체는 복사 값으로 안전하게 전달됨
  • 반환하는 건 포인터가 아님 → 메모리 침범 없음

📌 트랩은 다음과 같은 코드에서 발생함:

Item* getDanger() {
    Item temp;
    temp.data = 42;
    return &temp;  // ❌ 위험: 지역 변수의 주소 반환
}

정답: ①


✅ 2. 구조체 안에 배열 + 동적 배열 조합 (체크)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char name[10];   // 고정 배열
    char* nickname;  // 동적 문자열
} User;

int main() {
    User u;
    strcpy(u.name, "Kim");

    u.nickname = malloc(20);
    strcpy(u.nickname, "Tiger");

    printf("%s %s\n", u.name, u.nickname);
    free(u.nickname);
    return 0;
}

Q2. 출력 결과는?

① Kim Tiger
② 쓰레기 값
③ Segfault
④ 컴파일 에러


✅ 해설

  • 구조체 안에 char name[10] → 정적 배열이므로 strcpy() 가능
  • nickname은 포인터이므로 반드시 malloc() 후 사용

정답: ①

📌 흔한 실수:

User u;
strcpy(u.nickname, "Tiger");  // ❌ nickname이 아직 아무것도 안 가리킴!

✅ 3. typedef struct *Ptr 형태 트랩

#include <stdio.h>
#include <stdlib.h>

typedef struct Person {
    int age;
} *PersonPtr;

int main() {
    PersonPtr p = malloc(sizeof(struct Person));
    p->age = 30;
    printf("%d\n", p->age);
    free(p);
    return 0;
}

Q3. 출력 결과는?

① 30
② 쓰레기 값
③ 컴파일 에러
④ Segfault


✅ 해설

  • typedef struct Person *PersonPtr;PersonPtr포인터 타입으로 정의
  • malloc(sizeof(struct Person)) → OK
  • p->age = 30 → OK

📌 주의할 점: PersonPtr포인터 타입이라는 것을 헷갈리지 말아야 함

정답: ①


✅ 4. void 포인터 + 구조체 캐스팅 트랩

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int id;
} Node;

void printNode(void* p) {
    Node* n = (Node*)p;
    printf("%d\n", n->id);
}

int main() {
    Node* node = malloc(sizeof(Node));
    node->id = 77;

    printNode(node);
    free(node);
    return 0;
}

Q4. 출력 결과는?

① 77
② 쓰레기 값
③ 컴파일 에러
④ 런타임 에러


✅ 해설

  • void*는 모든 포인터를 받을 수 있는 범용 타입
  • Node* n = (Node*)p; → 올바른 캐스팅
  • printNode(node); → 구조체 주소 전달

정답: ①

📌 주의할 점:

  • void 포인터는 반드시 정확한 형변환 후 사용
  • 잘못된 형변환은 Undefined Behavior

🔁 요약

문제주제트랩 포인트정답
Q1구조체 반환값 복사 vs 참조
Q2구조체 내 배열malloc 여부
Q3typedef *포인터선언 혼동
Q4void* + 구조체캐스팅 필수

다음 심화 주제 추천

  • ✅ 구조체 배열 정렬 with qsort and strcmp()
  • ✅ 구조체 배열을 void*로 받아 처리하는 제너릭 함수
  • ✅ 얕은 복사와 깊은 복사 실제 예시 (복제 함수 직접 만들기)
  • ✅ 구조체 안에 함수 포인터 배열 만들기

원하는 방향이나 구체적 포인트 있으면 알려줘!
또는 “실전 실기처럼 종합 문제”도 내줄 수 있어.

아주 날카로운 질문이야!
실제 정보처리기사 실기나 C 언어 시험에서는 숫자가 잘 출력되는 정상 케이스뿐 아니라, 아래와 같은 **“비정상 동작이 정답이 되는 트랩 문제”**도 매우 자주 나와:


✅ 실전에서 자주 출제되는 "정상 출력 안 되는" 유형 (체크)

1. ❗ 지역 변수 주소 반환 → 런타임 오류 또는 경고

int* foo() {
    int x = 10;
    return &x;  // 위험: 함수 끝나면 x 사라짐
}

2. ❗ 동적 메모리 해제 후 사용 → use-after-free

int* p = malloc(sizeof(int));
free(p);
printf("%d\n", *p);  // 터지거나 쓰레기 값

3. ❗ 초기화하지 않은 포인터 접근 → Segmentation Fault (체크)

int* p;
*p = 5;  // 어디를 가리키는지 모르는데 값을 넣음

4. ❗ 구조체 포인터 vs 값 혼동 → 컴파일 에러

struct A { int x; };
struct A* p;
p.x = 3;  // ❌ p는 포인터인데 . 연산자 사용 (-> 써야 함)

📌 요약하면, “정답이 출력되는 것”만이 아니라 다음도 자주 정답이야:

유형설명예시 정답
컴파일 에러문법/타입 불일치struct A p; p->x = 1;
런타임 에러NULL 접근, 세그폴트int* p = NULL; *p = 10;
쓰레기 값초기화 안 함int a; printf("%d", a);
의도된 동작 아님shallow copy / 포인터 재사용strcpy(b.name, "hi"); (포인터 안 가리킴)

좋아! 지금부터 출력이 "정상적으로 되지 않아야 정답"인 실전 트랩 문제 5제를 출제할게. 각 문제는 다음 중 하나의 결과를 유발하도록 설계되어 있어:

  • ❌ 컴파일 에러
  • ❌ 런타임 에러 (세그폴트 등)
  • ❌ 쓰레기 값
  • ❌ 메모리 침해 (use-after-free, double free 등)
  • ❌ 잘못된 동작 (논리 버그, shallow copy 문제 등)

🔥 실전 트랩 문제 1 – 구조체 포인터 실수 (체크)

#include <stdio.h>

typedef struct {
    int value;
} Box;

int main() {
    Box* b;
    b->value = 10;
    printf("%d\n", b->value);
    return 0;
}

Q1. 결과는?

① 10
② 컴파일 에러
③ 쓰레기 값
④ 런타임 에러


✅ 해설:

  • Box* b;는 선언만 했고, malloc이나 초기화를 하지 않음
  • b->valueNULL이나 쓰레기 주소를 역참조
  • 실행 시 Segmentation Fault 발생 가능

정답: ④ 런타임 에러


🧨 실전 트랩 문제 2 – 구조체 얕은 복사 문제

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char* name;
} Person;

int main() {
    Person a;
    a.name = malloc(10);
    strcpy(a.name, "Jack");

    Person b = a;
    free(a.name);
    printf("%s\n", b.name);
    return 0;
}

Q2. 결과는?

① Jack
② 컴파일 에러
③ 쓰레기 값 or 런타임 에러
④ Segfault 없이 정상 출력


✅ 해설:

  • 구조체 복사 시 char* name도 shallow copy 됨
  • free(a.name) 하면 b.name도 같은 주소 → dangling pointer
  • b.name 출력 시 use-after-free → 런타임 에러 가능

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


🪤 실전 트랩 문제 3 – 함수 리턴 구조체 포인터 (체크)

#include <stdio.h>

typedef struct {
    int data;
} Item;

Item* get() {
    Item temp;
    temp.data = 77;
    return &temp;
}

int main() {
    Item* p = get();
    printf("%d\n", p->data);
    return 0;
}

Q3. 결과는?

① 77
② 컴파일 에러
③ 쓰레기 값
④ 런타임 에러


✅ 해설:

  • temp는 함수 안의 지역 변수
  • 주소를 반환하면, 함수 종료와 동시에 사라진 메모리를 참조하게 됨
  • p->data는 undefined behavior → 보통 쓰레기 값 출력

정답: ③ 쓰레기 값

  1. 구조체를 반환하는 함수의 위험 (복사 vs 참조) 와 비교
항목첫 번째 코드 (Item* get())두 번째 코드 (Item getItem())
반환 타입Item* (구조체의 주소 반환)Item (구조체 자체 복사 반환)
반환하는 메모리 위치함수 안의 지역 변수 (스택)지역 변수 복사 (값 복사)
함수 종료 후 메모리 상태지역 변수는 소멸함 → 잘못된 메모리 참조복사된 구조체는 main 함수에 안전하게 전달됨
실행 결과미정 (쓰레기값, 세그멘테이션 오류 가능)정상 동작, 안전하게 42 출력됨
  • ✅ 한 문장 핵심 요약
    *가 붙으면 "주소를 저장하는 변수(포인터)",
    안 붙으면 "실제 값을 저장하는 변수".

🧱 실전 트랩 문제 4 – void 포인터 잘못된 캐스팅 (체크)

#include <stdio.h>

typedef struct {
    int a, b;
} Data;

void print(void* p) {
    int* x = (int*)p;
    printf("%d\n", *x);
}

int main() {
    Data d = {5, 10};
    print(&d);
    return 0;
}

Q4. 출력 결과는?

① 5
② 10
③ 쓰레기 값
④ 컴파일 에러


✅ 해설:

  • 구조체 Data{int a, b} 이지만,
  • void*로 받아 int*로 변환해 첫 필드만 출력 → 이건 괜찮을 수도 있어.
  • BUT, 이 방식은 위험 → 멤버 타입이 바뀌면 터짐
  • 이 문제에선 5 출력은 가능하지만 매우 위험한 방식 → 정답은 의도된 동작 아님

정답: ①
(하지만 트랩 포인트로 삼는다면 실무에서 지양해야 할 방식!)


🧨 실전 트랩 문제 5 – 이중 free()

#include <stdio.h>
#include <stdlib.h>

int main() {
    int* a = malloc(10);
    free(a);
    free(a);  // 두 번 해제
    return 0;
}

Q5. 결과는?

① 컴파일 에러
② 정상 실행
③ 런타임 에러 (Double free)
④ 쓰레기 값 출력


✅ 해설:

  • C에서는 free() 후 다시 해제하면 Double Free
  • 대부분 시스템에서 즉시 런타임 에러

정답: ③ 런타임 에러 (Double Free)


🧠 총정리 표

문제핵심 트랩정답이유
Q1미초기화 포인터 역참조Segfault
Q2shallow copy + freeuse-after-free
Q3지역 변수 주소 반환dangling pointer
Q4잘못된 void* 캐스팅운 좋으면 OK, but 위험
Q5free 두 번런타임 에러

좋아, 이번엔 숫자가 많지 않지만 계산 흐름에서 헷갈리는 구조체 문제를 줄게.
한눈에 보기엔 단순해 보이지만, 구조체 복사, 포인터 접근, 값 변경 흐름이 복잡하게 얽혀 있어 실수하기 쉬운 스타일로 구성할게.


🧠 문제: 구조체 복사와 포인터 수정 트랩

#include <stdio.h>

typedef struct {
    int x;
} Point;

void update(Point a, Point* b) {
    a.x += 1;
    b->x += 2;
}

int main() {
    Point p1 = {5};
    Point p2 = p1;

    update(p1, &p2);

    printf("%d %d\n", p1.x, p2.x);
    return 0;
}

❓ Q. 출력 결과는?

5 7
6 7
6 6
7 7


✅ 풀이 & 해설

📌 변수 상태 정리

  • p1 = {5}

  • p2 = p1; → 복사본 {5}
    → 이 시점:

    p1.x = 5  
    p2.x = 5

📌 함수 호출

update(p1, &p2);

함수 매개변수:

  • ap1복사본 → 값으로 전달됨 (지역 복사본)
  • bp2주소 → 포인터 전달 (원본 수정됨)

📌 함수 내부:

a.x += 1;     // a는 복사본 → 원본 영향 없음
b->x += 2;    // b는 포인터 → 실제 p2.x에 적용됨

→ 실행 후 상태:

a.x = 6 (지역 변수, 소멸됨)
p1.x = 5 (원본 유지)
p2.x = 7 (2 더함)

✅ 정답: ① 5 7


🧠 포인트 요약

  • 구조체를 함수에 전달할 때 값으로 전달되면, 원본은 수정되지 않음
  • 포인터(Point*)로 전달하면, 원본 수정 가능
  • p2 = p1;은 복사 (shallow copy지만 값형 구조체이므로 괜찮음)

0개의 댓글