임베디드기사 실기 12 — 프로그램 작성 (C 프로그래밍: 포인터·메모리·출력 결과 계산)
임베디드기사 실기 12 — 프로그램 작성 (C 프로그래밍: 포인터·메모리·출력 결과 계산)
출제기준 4-2: 임베디드 프로그램 작성하기
코드를 읽고 출력 결과를 손으로 계산하는 문제가 해마다 나와요.
문제 수는 적어도 점수는 확실히 가져갈 수 있는 구간입니다.
📎 필기 시리즈의 프로그래밍 언어 활용 편과 함께 보면 좋아요. (링크 추가 예정)
실기 출제 포인트 ⭐⭐⭐
| 키워드 | 핵심 내용 | 빈도 | 기출 |
| 출력 결과 계산 | 포인터·반복문·서식 문자 혼합 | ⭐⭐⭐ | 2017 Q11 = 2021 Q8, 2018 Q6, 2018 Q9, 2021 Q13, 2025 Q16 |
| 포인터 / 구조체 | *(s+2), p->멤버 = (*p).멤버 |
⭐⭐ | 2018 Q6, 2021 Q14 = 2022 Q5 |
| 메모리 할당·해제 | malloc / free, 이중 해제 |
⭐⭐⭐ | 2018 Q2 = 2024 Q7 (free), 2023 Q8 (이중 해제) |
| 공용체 union | 멤버가 메모리를 공유 | ⭐⭐ | 2017 Q1 = 2023 Q17 |
| 제어문 | break, continue |
⭐ | 2020 Q9 |
| 표준 입출력 | stdin / stdout / stderr, gets → fgets |
⭐ | 2018 Q4, 2021 Q18 |
| 오버라이딩 / 오버로딩 | 재정의 vs 중복 정의 | ⭐⭐ | 2017 Q4 = 2024 Q3, (2016 Q5 Java 상속 출력) |
| 정렬 / 트리 | 선택 정렬 중간 단계, 중위 순회 | ⭐ | 2020 Q7, 2025 Q16 |
키워드 박스 — 답안에 반드시 들어가야 할 단어
-> : 구조체 포인터의 멤버 접근 ( p->x == (*p).x )
free() : malloc으로 할당한 메모리 해제 / 같은 주소를 두 번 free하면 이중 해제 오류
union : 모든 멤버가 같은 메모리를 공유 (크기 = 가장 큰 멤버)
%X / %x : 16진수 대문자 / 소문자
break : 반복 즉시 종료 / continue : 이번 회차만 건너뜀
overriding : 상속받은 메서드 재정의 / overloading : 같은 이름, 다른 매개변수
서식 문자와 출력 결과 계산 ⭐⭐⭐
기출 (2017 Q11 = 2021 Q8) 다음 C 코드의 실행 결과를 적으시오.
(printf의 %X로 10진수 15를 출력하는 코드)
→ F
| 서식 | 의미 | 예 |
%d |
부호 있는 10진수 | -1 → -1 |
%u |
부호 없는 10진수 | |
%x |
16진수 소문자 | 255 → ff |
%X |
16진수 대문자 | 255 → FF |
%o |
8진수 | 8 → 10 |
%c / %s |
문자 / 문자열 | |
%p |
포인터 주소 |
위 표의 예시값은 컴파일해서 확인한 결과예요(
%X255 → FF,%o8 → 10).
⚠️ 함정: 대소문자! %X면 F, %x면 f 입니다. 문제 코드의 서식 문자를 먼저 보세요.
삼항 연산자 계산
기출 (2021 Q13) 다음 C 코드의 출력 결과는?
(a = 31, b = 27, c = 19, 식: ((a = (a > b) ? a : b) ? a : c))
→ 31
1단계: (a > b) ? a : b → (31 > 27) ? 31 : 27 → 31
2단계: a = 31 (할당식의 값은 대입된 값 31)
3단계: (31) ? a : c → 31은 0이 아니므로 참 → a → 31
규칙: 안쪽 조건부터 계산하고, 할당식의 값은 대입된 값이며, 0이 아니면 참이에요.
포인터와 구조체 ⭐⭐⭐
포인터 연산
기출 (2018 Q6) int data[] = {10, 20, 30, 40, 50}; int *s = data; 일 때 *(s+2)+10을 출력하면?
→ 40
s + 2 → &data[2] (int 2칸 이동)
*(s + 2) → data[2] = 30
30 + 10 → 40
기출 (2018 Q9) int *s = malloc(sizeof(int)); *s = 10; printf("%d", *s); 의 출력은?
→ 10
| 표현 | 의미 | 위 배열 예 |
*(s+2) |
2칸 뒤 원소의 값 | 30 |
*s+2 |
첫 원소 값에 2를 더함 | 12 |
*(s+2)+10 |
2칸 뒤 원소 값에 10을 더함 | 40 |
위 세 값은 직접 컴파일해서 확인했어요(30 / 12 / 40).
⚠️ 함정: *s+2와 *(s+2)는 우선순위가 달라 결과가 다릅니다. 괄호 위치를 꼭 확인하세요.
구조체 포인터 멤버 접근
기출 (2021 Q14 = 2022 Q5)
struct student *p = &s; 일 때 printf("%d %s %c", ( )) 의 빈칸에 들어갈 수 있는 것을 모두 고르시오.
| 보기 | 표현 | 판정 |
| (A) | p.number, p.name, p.grade |
❌ 포인터에는 . 사용 불가 |
| (B) | p->number, p->name, p->grade |
⭕ |
| (C) | (*p).number, (*p).name, (*p).grade |
⭕ |
| (D) | (*p->number), (*p->name), (*p->grade) |
❌ |
→ (B), (C)
struct student { int number; char name[20]; char grade; };
struct student s = {2024001, "홍길동", 'A'};
struct student *p = &s;
p->number == (*p).number /* 두 표현은 동일 */
동적 메모리: malloc / free ⭐⭐⭐
기출 (2018 Q2 = 2024 Q7) malloc()으로 할당받은 메모리를 해제하는 함수는?
→ free()
기출 (2023 Q8) 다음 C 코드에서 오류가 발생하는 부분을 찾고 이유를 설명하시오.
ptr1 = malloc(512); /* 메모리 A 할당 */
ptr2 = malloc(512); /* 메모리 B 할당 */
ptr2 = ptr1; /* ptr2가 가리키던 B의 주소를 잃음 → 메모리 누수 */
free(ptr2); /* A 해제 */
free(ptr1); /* A를 다시 해제 → 이중 해제(Double Free) 오류 */
→ 정리본 답: 오류 행은 ptr2 = ptr1; 과 free(ptr1); (이중 해제), 그리고 ptr2 = ptr1; 때문에 B 주소를 잃어 메모리 누수도 발생
메모리 상태
처음 : ptr1 → [A] ptr2 → [B]
대입 : ptr1 → [A] ptr2 → [A] [B]는 아무도 가리키지 않음 (누수)
free(ptr2) 후 : [A] 해제됨
free(ptr1) : 이미 해제된 [A]를 또 해제 → 오류
⚠️ 함정: 이중 해제를 묻는 기출은 2023 Q8 입니다.
2018 Q2 = 2024 Q7은 "해제 함수 이름(free)"만 묻는 단답이에요.
정리 자료에 두 가지가 한꺼번에 묶여 있어서 헷갈리기 쉬워요.
공용체 (union) ⭐⭐
기출 (2017 Q1 = 2023 Q17) C 언어에서 공용체를 나타내는 키워드는?
→ union
| 구분 | struct | union |
| 메모리 | 멤버마다 별도 공간 | 모든 멤버가 같은 공간 공유 |
| 크기 | 멤버 크기의 합 (+ 정렬 패딩) | 가장 큰 멤버의 크기 |
| 값 | 모든 멤버 동시에 유효 | 마지막에 쓴 멤버만 유효 |
union U { char c; int i; double d; }; /* sizeof = 8 (가장 큰 double) */
struct S { char c; int i; double d; }; /* sizeof = 16 (패딩 포함) */
위 크기(8 / 16)는 64비트 환경에서 직접 컴파일해 확인한 값이에요.
구조체 크기는 컴파일러·정렬 규칙에 따라 달라질 수 있습니다.
제어문과 표준 입출력 ⭐
break / continue
기출 (2020 Q9) for, while, do while 의 반복 과정을 중단시킬 수 있는 명령어는?
→ break
| 명령 | 동작 |
break |
반복문을 즉시 종료 |
continue |
현재 회차만 건너뛰고 다음 회차 진행 |
return |
함수 자체를 종료 |
for (int i = 0; i < 6; i++) {
if (i == 2) continue; /* 2만 건너뜀 */
if (i == 4) break; /* 4에서 종료 */
printf("%d ", i);
}
/* 출력: 0 1 3 (직접 실행해 확인한 결과) */
표준 입출력
기출 (2018 Q4) ① 공백을 포함한 한 줄을 입력받는 함수, ② 문자 하나를 입력받는 함수는?
→ 정리본 답: ① gets() (→ fgets() 권장), ② getchar()
⚠️ 함정: gets()는 버퍼 오버플로 위험 때문에 C11 표준에서 삭제됐어요.
시험 정답은 gets로 적혀 있지만, fgets(str, size, stdin)가 안전한 방법이라는 점도 함께 기억해두세요.
기출 (2021 Q18) 표준 입출력 3가지 / 임베디드에서 표준 입출력을 구현하는 방식은?
→ stdin / stdout / stderr, 구현은 UART(직렬 통신) (4편의 UART와 연결)
객체지향: 오버라이딩 vs 오버로딩 ⭐⭐
기출 (2017 Q4 = 2024 Q3) 자식 클래스가 부모에게서 상속받은 메서드를 자신에 맞게 재정의하는 기법은?
→ 오버라이딩 (Overriding)
| 구분 | 오버라이딩 | 오버로딩 |
| 의미 | 상속받은 메서드를 재정의 | 같은 이름을 매개변수를 달리해 중복 정의 |
| 관계 | 부모 - 자식 (상속) | 같은 클래스 안 |
| 시그니처 | 이름·매개변수·반환형 동일 | 매개변수 수/타입이 다름 |
| 결정 시점 | 실행 시(동적 바인딩) | 컴파일 시 |
기출 (2016 Q5) Java 상속 코드의 출력 결과를 쓰는 문제
(정리본 답: p.x = 100 / Child Method / c.x = 200 / Child Method)
규칙: 필드(변수) → 참조 변수의 "선언 타입"을 따름 (Parent p 이면 Parent의 x)
메서드 → 실제 "인스턴스 타입"을 따름 (오버라이딩된 Child의 메서드 실행)
코드 본문은 정리본에 빠져 있어서, 위는 정리본이 설명한 규칙과 정답 위주로 정리했어요.
정렬과 트리 ⭐
기출 (2020 Q7) 3 2 1 5 4를 선택 정렬할 때 마지막 단계를 수행하기 직전의 결과는?
→ 1 2 3 5 4
선택 정렬(오름차순): 매 단계마다 남은 구간의 최솟값을 찾아 맨 앞과 교환 (n개면 n-1단계)
초기 : 3 2 1 5 4
1단계 : 1 2 3 5 4 (1과 3 교환)
2단계 : 1 2 3 5 4 (교환 없음)
3단계 : 1 2 3 5 4 (교환 없음)
4단계 : 1 2 3 4 5 (5와 4 교환 = 마지막 단계)
→ 마지막(4단계) 직전 = 1 2 3 5 4
위 단계별 결과는 직접 코드로 돌려 확인했어요.
기출 (2025 Q16) 이진 트리를 생성하고, 배열로 바꿔 정렬한 뒤 다시 트리로 만들어 중위 순회 결과를 출력하는 C 코드의 출력은?
→ 정리본 정답: 1234567
⚠️ 함정: 이 문항은 정리본에 코드가 생략돼 있어 답을 직접 검증할 수 없어요.
원리만 기억하세요 — 정렬된 이진 탐색 트리(BST)를 중위 순회하면 오름차순으로 나옵니다.
📌 개념 보강 — 트리 순회 (기출 직접 문항 없음)
전위(Pre-order) : Root → Left → Right
중위(In-order) : Left → Root → Right ← BST에서는 오름차순
후위(Post-order) : Left → Right → Root
기출 문제 모음
Q. printf("%X", 15) 의 출력은? (2017·2021)
A. F
Q. int data[]={10,20,30,40,50}; int *s=data; 일 때 *(s+2)+10 은? (2018 Q6)
A. 40
Q. 구조체 포인터 p로 멤버에 접근하는 올바른 방법은? (2021·2022)
A. p->number, (*p).number
Q. malloc으로 할당한 메모리를 해제하는 함수는? (2018·2024)
A. free()
Q. ptr2 = ptr1; 후 free(ptr2); free(ptr1); 의 문제는? (2023 Q8)
A. 이중 해제(Double Free), 그리고 ptr2가 가리키던 메모리의 누수
Q. 모든 멤버가 같은 메모리를 공유하는 C의 키워드는? (2017·2023)
A. union
Q. 상속받은 메서드를 재정의하는 것은? (2017·2024)
A. 오버라이딩
💡 실무에서는?
- 이중 해제·메모리 누수 — 임베디드에서는 메모리가 작아서 누수가 곧 장애로 이어져요.
valgrind같은 도구로 확인하거나, 할당·해제를 쌍으로 묶는 규칙을 코딩 표준에 넣는 게 일반적입니다. gets()사용 금지 — 보안 점검에서 가장 먼저 걸리는 함수예요.
정보보안 쪽 시큐어 코딩 점검 항목(버퍼 오버플로)과 그대로 연결됩니다.- union — 레지스터 값을 비트 필드와 정수로 번갈아 보거나, 바이트 단위로 쪼개 볼 때 쓰입니다.
다만 엔디안에 따라 결과가 달라질 수 있어 이식성에 주의해야 해요. - 출력 결과 계산 — 실무에서 코드 리뷰할 때 하는 "머릿속 실행"과 같은 훈련이에요.
핵심 정리
✅ %X = 16진수 대문자 (15 → F), %x = 소문자 (15 → f)
✅ *(s+2) = 2칸 뒤 값 / *s+2 = 첫 값 + 2 → 괄호 주의
✅ p->x == (*p).x (p.x 는 오류)
✅ free() = malloc 해제 / 같은 주소 두 번 free = 이중 해제 (2023 Q8)
✅ union = 같은 메모리 공유, 크기는 가장 큰 멤버
✅ break = 즉시 종료 / continue = 이번 회차만 건너뜀
✅ gets 대신 fgets (표준 입출력은 stdin / stdout / stderr)
✅ 오버라이딩 = 재정의 / 오버로딩 = 매개변수만 다르게
✅ 선택 정렬은 n-1단계, BST 중위 순회는 오름차순
다음 편에서는 개발도구 및 테스팅 기법(출제기준 4-3)으로 이어갑니다!
궁금한 점은 댓글로 남겨주세요!