자격증 정복/컴퓨터시스템기사

컴퓨터시스템기사 실기 08 — 프로그래밍 언어 활용 (C 출력 예측·포인터·구조체·객체지향·스크립트

올드 IT직장인 2026. 10. 18. 00:30

컴퓨터시스템기사 실기 08 — 프로그래밍 언어 활용 (C 출력 예측·포인터·구조체·객체지향·스크립트)

출제기준 코드: 5-1-1 프로그램 설계서 확인 · 5-1-2 구조적 언어로 애플리케이션 작성 · 5-1-3 오류 식별·수정
/ 5-2 객체지향 언어 활용 / 5-3 스크립트 활용
시험일: 2026-10-25 / 시리즈: 컴퓨터시스템기사 실기 08편

 

실기 시리즈 여덟 번째 글입니다.

이번 편은 대단원 5 (프로그래밍 언어 활용) 입니다.


이 단원은 두 종류 문제가 나옵니다.
① C 코드 출력 결과 쓰기 (가장 확실한 기출 유형)
② 빈칸 채우기 / 오류 수정 (B형에서 반복)


객체지향과 스크립트는 2026 신 출제기준에서 새로 들어온 항목이라 기출이 없지만, 코드를 읽고 결과를 예측하는 형태로 나올 가능성이 높아서 직접 풀어 본 예제로 대비합니다. (아래에서 "직접 만든 예제"는 기출이 아니라 제가 손으로 검산한 연습용입니다.)


1. 실기 출제 포인트

구분 형태 중요도
C 출력 예측 비트 연산, 반복문, 배열 합, 문자열, 정렬 코드 ⭐⭐⭐
빈칸 채우기 재귀(2진수 변환), 대소문자 변환, scanf 인자 ⭐⭐⭐
포인터·배열 주소 연산, a[i] ≡ *(a+i), 2차원 배열 ⭐⭐
구조체·공용체 멤버 접근, 크기 계산(패딩), union 공유 ⭐⭐
객체지향 개념 캡슐화·상속·다형성·오버로딩/오버라이딩 ⭐⭐ (신규)
스크립트 Python/Shell 출력 예측, 파이프·리다이렉션 ⭐⭐ (신규)
동적 할당 malloc/free, 누수 ⭐

💡 답안 키워드 박스
후위/전위 증감 정수 나눗셈 비트 연산(&, ^, |, <<, >>) 널 종료 문자 포인터 연산 패딩 공용체 캡슐화 상속 다형성(오버라이딩) 인터프리터 파이프 리다이렉션


2. C 출력 예측 — 규칙부터 (5-1-2) ⭐⭐⭐

2-1. 꼭 확인할 것

항목 규칙
전위/후위 증감 ++i는 증가 후 사용, i++는 사용 후 증가
정수 나눗셈 7/2 = 3, 7%2 = 1, 7/2.0 = 3.5
비트 연산 &(AND) |(OR) ^(XOR) ~(NOT) << >>
printf 서식 %d 정수, %c 문자, %s 문자열, %f 실수, %x 16진수
공백·개행 printf("%d", x) 에 공백이 없으면 숫자가 붙어서 출력
문자열 끝에 널 문자 \0 자동 추가, %s는 \0까지 출력

 

⚠️ 정렬 코드에서 printf가 어디에 있는지 꼭 보세요.

02편의 41234는 sort 함수 안에서 len(4)을 먼저 찍어서 나온 값입니다.

 

2-2. 기출 출력 예측 모음

출처 코드 핵심 출력
A 2020-3 i=24, z=22; a=i&z; b=i^z; printf("%d %d", a, b) 16 14
A 2020 통합 for(a=1;a<=15;a++){ if(a%2==0 || a%3==0) continue; printf("%d",a); } 1571113
A 2021-3 배열 {20,40,60,80,100} 의 합을 for로 누적 300
A 2021-3 2차원 문자 배열 {"ABC","DEF","GHI","JKL"} 에서 printf("%s", a[1]) DEF
A 2021-1·2025-1 ct = {95,50,80,35,70} 내림차순 선택 정렬 후 %d로 연속 출력 9580705035
A 2022-1 버블 정렬 함수 안에서 len 출력 후 main에서 배열 출력 41234

 

 

비트 연산 풀이 (A 2020-3)

24 = 0001 1000
22 = 0001 0110
AND = 0001 0000 = 16
XOR = 0000 1110 = 14      → "16 14"

 

 

조건 필터 풀이 (A 2020 통합)

1~15 중 2의 배수 또는 3의 배수는 continue(출력 생략)
남는 수: 1, 5, 7, 11, 13  → 붙여서 "1571113"

 

2-3. 직접 만든 연습 예제 (기출 아님, 손으로 검산)

① 증감 연산자

int a = 5;
int b = a++;     /* b = 5, a = 6 */
int c = ++a;     /* a = 7, c = 7 */
printf("%d %d %d", a, b, c);      →  7 5 7

 

 

② 정수 나눗셈과 연산 순서

int x = 10 / 4 * 2;      /* 10/4=2 → 2*2 = 4 */
double y = 10 / 4.0 * 2; /* 2.5 * 2 = 5.0 */
printf("%d %.1f", x, y);          →  4 5.0

 

 

③ 시프트

printf("%d %d", 1 << 4, 32 >> 2); →  16 8

 

 

④ 무한 루프와 break (A 2020 통합 유형)

for ( ; ; ) {            /* 빈칸 정답: ;; (조건 생략 = 항상 참) */
    scanf("%d", &a);
    if (a == 777) break;
}

3. 배열과 포인터 ⭐⭐

a[i]  ≡  *(a + i)        배열 이름 = 첫 요소의 주소
p + 1  = p가 가리키는 자료형의 크기(sizeof)만큼 이동

 

 

직접 만든 예제

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;
printf("%d %d %d\n", *p, *(p + 2), p[4]);    →  10 30 50
p++;
printf("%d", *p);                            →  20

 

 

문자열

char s[] = "ABC";
sizeof(s) = 4      (A,B,C,\0)
strlen(s) = 3      (널 문자 제외)

 

 

값 전달 vs 주소 전달 (swap 함수)

void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; }   /* 호출: swap(&a, &b) */

swap(int x, int y)처럼 값으로 받으면 원본이 바뀌지 않습니다.

 

⚠️ 함정: 함수에 배열을 넘기면 포인터가 전달되므로 함수 안의 sizeof(arr) 는 배열 전체 크기가 아니라 포인터 크기(64비트에서 8바이트)입니다. 02편의 sizeof(ct) / sizeof(int) 는 main 안에서 계산했기에 4가 됐습니다.


4. 구조체와 공용체 ⭐⭐

4-1. 구조체 (기출 B 2021-11 Q5)

typedef struct Complex {
    double real;
    double imag;
} Complex;

int main(void) {
    Complex c1, c2;
    scanf("%lf %lf", &(①), &(②));     /* 첫 번째 복소수: 실수부, 허수부 */
    scanf("%lf %lf", &(③), &(④));     /* 두 번째 복소수 */
    complex_add(c1, c2);
}

 

정답(복원본): c1.real, c2.real, c1.imag, c2.imag

🔎 순서 주의: 코드 흐름대로라면 ①c1.real ②c1.imag ③c2.real ④c2.imag 가 자연스럽습니다.
복원본은 정답 열거 순서가 이와 다르게 적혀 있어 번호 대응이 불분명합니다.
시험장에서는 괄호 번호에 맞춰 "①c1.real ②c1.imag ③c2.real ④c2.imag"처럼 번호와 함께 쓰는 것이 안전합니다.
(또한 원문의 complex_add 함수는 return이 빠져 있었습니다.)

  • 멤버 접근: 변수는 . (c1.real), 포인터는 -> (p->real)
  • scanf의 대상은 주소여야 하므로 & 필요 (&c1.real)

 

4-2. 구조체 크기 (패딩) — 직접 만든 예제

struct A { char a; int b; char c; };   /* 1 + (3 패딩) + 4 + 1 + (3 패딩) = 12 */
struct B { char a; char c; int b; };   /* 1 + 1 + (2 패딩) + 4 = 8 */

→ 같은 멤버라도 선언 순서에 따라 크기가 다릅니다. (int 4바이트 정렬 가정)

 

4-3. 공용체 (union)

union U { int i; double d; char c; };
sizeof(union U) = 8      (가장 큰 멤버 double 크기, 모든 멤버가 같은 메모리 공유)
  • 한 번에 하나의 멤버만 유효. 마지막에 쓴 멤버만 의미 있음
  • 용도: 같은 메모리를 다른 형식으로 해석 (패킷 헤더 필드, 프로토콜 파싱 — 07편의 헤더 구조와 연결)

5. 오류 식별과 수정 (5-1-3) ⭐⭐⭐

5-1. 재귀로 10진수를 2진수 출력 (B 2019-11 Q11)

void binary(int n) {
    if (n > 0) {
        binary( (1) );        /* 정답: n/2 */
        printf("%d", (2) );   /* 정답: n%2 */
    }
}

 

 

22 입력 추적: 재귀가 끝까지 내려간 뒤 돌아오면서 출력하므로 아래 자리부터 거꾸로 쌓입니다.

binary(22) → binary(11) → binary(5) → binary(2) → binary(1) → binary(0)(종료)
출력(복귀 순서): 1%2=1, 2%2=0, 5%2=1, 11%2=1, 22%2=0   →  10110

🔎 원문 코드에는 main이 binary(n)을 호출하지 않고, void main에서 return 0을 쓰는 오류가 있었습니다.
"오류를 찾아 수정하라"가 나오면 이런 항목(호출 누락, 반환형 불일치)도 체크하세요.

5-2. 대소문자 변환 (B 2020-11 Q6)

char ch;
scanf("%c", &ch);
if (ch >= (1) && ch <= (2))       /* 정답: 'a', 'z' */
    ch = ch - (1) + (3);          /* 정답: 'a' ... 'A'  →  소문자를 대문자로 */
printf("%c", ch);

 

정답: (1) 'a', (2) 'z', (3) 'A'
→ 소문자 ch에서 'a'를 빼 알파벳 순번을 구한 뒤 'A'를 더해 대문자로 변환합니다 (예: 'c' - 'a' + 'A' = 'C').

🔎 원문 코드는 변수명이 ch와 c로 섞여 있었습니다.
변수 선언과 사용이 일치하는지 확인하는 것이 오류 식별의 첫 단계입니다.
따옴표는 작은따옴표(문자).

5-3. 오류 식별 체크리스트

□ 변수 선언 이름과 사용 이름 일치 (ch / c)
□ 조건식 괄호, && / || 올바른 사용
□ scanf 형식 지정자와 & (주소 연산자)
□ 함수 return 누락, 반환형 일치
□ 재귀 함수의 종료 조건
□ 배열 인덱스 범위 (0 ~ n-1)
□ 세미콜론, 중괄호 짝
□ = (대입) 과 == (비교) 혼동

6. 메모리 동적 할당 ⭐ (보조)

int *p = (int *)malloc(5 * sizeof(int));   /* 힙에 20바이트 */
if (p == NULL) { /* 할당 실패 처리 */ }
/* 사용 */
free(p);
p = NULL;                                   /* 해제 후 NULL로 (dangling 방지) */
함수 설명
malloc(n) n바이트 할당, 초기화 안 함
calloc(c, s) c×s 바이트 할당, 0으로 초기화
realloc(p, n) 크기 변경
free(p) 해제

 

대표 오류: 메모리 누수(해제 누락), 해제 후 사용(dangling pointer), 이중 해제(double free).
(실기 출제기준 5-1 세세항목에는 직접 명시되지 않아 우선순위는 낮습니다. 개념 정의 문제 정도로 대비하세요.)


7. 객체지향 프로그래밍 (5-2) ⭐⭐ 신규

7-1. 핵심 개념

개념 설명
클래스 / 객체(인스턴스) 설계도 / 설계도로 만든 실체
캡슐화 데이터와 메서드를 묶고 접근 제한으로 정보 은닉
상속 부모 클래스의 속성·기능을 자식이 물려받아 재사용·확장
다형성 같은 메시지(메서드 호출)에 대상 객체에 따라 다르게 동작
추상화 공통 특성만 뽑아 단순화 (추상 클래스, 인터페이스)
비교 오버로딩(Overloading) 오버라이딩(Overriding)
정의 같은 이름, 매개변수가 다른 메서드 여러 개 부모의 메서드를 자식이 재정의
관계 같은 클래스 안 상속 관계
결정 시점 컴파일 시 (정적) 실행 시 (동적 바인딩)

 

접근 제한자: public(어디서나) > protected(자식·같은 패키지) > default(같은 패키지) > private(해당 클래스만). UML에서는 +(public), #(protected), -(private) 로 표기합니다 (프로그램 설계서 확인의 핵심 기호).

 

7-2. 출력 예측 (직접 만든 예제, 기출 아님)

class Animal { void sound() { System.out.println("..."); } }
class Dog extends Animal { void sound() { System.out.println("멍멍"); } }

public class Main {
    public static void main(String[] args) {
        Animal x = new Dog();     /* 업캐스팅 */
        x.sound();                /* 실제 객체 타입(Dog)의 메서드 호출 */
    }
}

→ 멍멍 (오버라이딩 + 동적 바인딩)

 

class Counter { static int cnt = 0; Counter() { cnt++; } }
/* new Counter(); new Counter(); System.out.println(Counter.cnt); */

→ 2 (static 변수는 모든 객체가 공유)

 

⚠️ 함정: 변수 타입(Animal)이 아니라 실제 생성된 객체(Dog) 의 오버라이딩된 메서드가 실행됩니다.

필드는 오버라이딩되지 않습니다.


8. 스크립트 언어 (5-3) ⭐⭐ 신규

8-1. 종류와 특징

언어 용도
Shell (Bash) 리눅스 운영 자동화, 배치
Python 자동화, 데이터 처리, 보안 도구
JavaScript 웹 브라우저·Node.js
PowerShell 윈도우 관리 자동화
Perl / PHP / Ruby 텍스트 처리 / 웹 서버 / 웹 프레임워크

 

공통 특징: 인터프리터 방식(소스를 한 줄씩 해석·실행, 별도 컴파일 불필요), 동적 타입, 개발 속도 빠름, 실행 속도는 컴파일 언어보다 느림.
(컴파일러는 소스 전체를 목적 코드로 번역 → 실행 빠름. 기출: 고급 언어를 기계어로 변환해 목적 프로그램을 만드는 번역기 = 컴파일러, A 2018-1)

 

8-2. Python 출력 예측 (직접 만든 예제)

a = [1, 2, 3, 4, 5]
print(a[1:4])                           # [2, 3, 4]   (끝 인덱스 4는 제외)
print(a[-1])                            # 5           (-1은 마지막)
print(sum(x for x in a if x % 2 == 1))  # 9           (1+3+5)
print(7 // 2, 7 % 2, 7 / 2)             # 3 1 3.5
print([x * x for x in range(4)])        # [0, 1, 4, 9]
print(list(range(1, 10, 3)))            # [1, 4, 7]

 

⚠️ 파이썬은 /가 항상 실수 나눗셈, 정수 나눗셈은 //. 들여쓰기가 블록을 결정합니다 (중괄호 없음).

 

8-3. Shell 기본 (직접 만든 예제)

for i in 1 2 3; do echo $((i*i)); done     # 1 4 9 (줄바꿈 출력)

x=5
if [ $x -gt 3 ]; then echo big; else echo small; fi    # big

echo "a b c" | wc -w                        # 3  (파이프: 앞 출력 → 뒤 입력)
ls /nonexistent 2>/dev/null; echo $?        # 2 (직전 명령 종료 코드, 0이 성공)
기호 의미
| 파이프 (앞 명령 출력을 뒤 명령 입력으로)
> / >> 파일로 출력 (덮어쓰기 / 추가)
2>&1 표준 오류를 표준 출력으로 합침
$? 직전 명령의 종료 코드
$1, $@ 첫 번째 인자, 모든 인자

 

⚠️ 쉘에서 변수 대입은 x=5 (= 양옆에 공백 금지), 사용은 $x.


9. 기출·예상 Q&A 모음

Q1. i=24, z=22; a=i&z; b=i^z; printf("%d %d", a, b); 의 출력은? (A 2020-3)
A. 16 14.

 

Q2. 1~15 중 2의 배수 또는 3의 배수를 건너뛰고 출력하는 반복문의 결과는? (A 2020 통합)
A. 1571113.

 

Q3. 배열 {20,40,60,80,100} 누적 합 출력? (A 2021-3)
A. 300.

 

Q4. 10진수를 2진수로 출력하는 재귀 함수의 빈칸은? (B 2019-11)
A. n/2, n%2 (22 → 10110).

 

Q5. 소문자를 대문자로 바꾸는 코드의 빈칸은? (B 2020-11)
A. 'a', 'z', 'A'.

 

Q6. 777을 입력하면 종료하는 무한 루프 for ( ① )의 ①은? (A 2020 통합)
A. ;; (모두 생략 = 항상 참).

 

Q7. 고급 언어를 기계어로 번역해 목적 프로그램을 생성하는 번역기는? (A 2018-1)
A. 컴파일러 (한 줄씩 해석해 즉시 실행하는 것은 인터프리터).

 

Q8. 같은 이름, 다른 매개변수로 메서드를 여러 개 정의하는 것은? (예상)
A. 오버로딩 (부모 메서드 재정의는 오버라이딩).


10. ⚠️ 함정 패턴 정리

  1. printf의 공백·개행 유무 — 숫자가 붙어서 출력되는지 확인
  2. 정수 나눗셈과 % 의 부호·몫
  3. 전위/후위 증감의 평가 시점
  4. = 와 == 혼동 (조건식에서의 대입)
  5. scanf에 & 누락 (문자열 배열은 & 불필요)
  6. sizeof(배열) — 함수 안에서는 포인터 크기
  7. 구조체 크기 — 단순 합이 아니라 패딩 포함
  8. union 크기 = 가장 큰 멤버
  9. 오버로딩 ↔ 오버라이딩, 정적 ↔ 동적 바인딩
  10. Python / vs //, 슬라이스 끝 인덱스 제외

11. 💡 실무에서는?

  • C의 가장 흔한 보안 결함은 메모리 오류입니다. gets, strcpy, sprintf 처럼 길이를 검사하지 않는 함수는 버퍼 오버플로우의 원인이고, fgets, strncpy, snprintf 로 대체합니다. 시험에서 배운 "배열 인덱스 범위"가 실무에서는 취약점 점검 1순위 항목입니다. 개발 단계에서 gcc -Wall -Wextra, cppcheck, clang-tidy, 런타임 -fsanitize=address(AddressSanitizer)를 쓰면 대부분 걸러집니다.
  • 구조체 패딩/정렬은 네트워크 패킷·파일 포맷을 구조체로 직접 매핑할 때 문제를 일으킵니다 (#pragma pack, 엔디안 변환 ntohl). 07편의 헤더 필드와 이어지는 지점입니다.
  • 객체 역직렬화 취약점 (Java ObjectInputStream, Python pickle): 신뢰할 수 없는 데이터를 역직렬화하면 원격 코드 실행이 가능합니다. 객체지향의 "객체 복원"이 공격 표면이 되는 사례입니다.
  • 쉘 스크립트 방어적 작성: 스크립트 상단에 set -euo pipefail, 변수는 항상 "$var" 로 인용 (공백·특수문자·명령 주입 방지), 입력값은 검증 후 사용, 비밀번호·토큰 하드코딩 금지(환경 변수·시크릿 매니저). 정적 점검은 ShellCheck가 유용합니다.
  • Python 운영 스크립트: 가상환경(venv)으로 의존성 분리, subprocess 호출 시 shell=True 지양(주입 위험), 로깅·예외 처리를 넣어 cron/자동화에서 조용히 실패하지 않게 합니다.
  • 로그 분석 자동화 한 줄: grep ERROR app.log | awk '{print $1}' | sort | uniq -c | sort -rn | head — 파이프 개념 그대로입니다.

12. 핵심 정리

✅ 출력 예측 순서: 변수 값 추적 → 연산자 규칙 → printf 서식(공백/개행)
✅ i&z=16, i^z=14 (24, 22) / 1~15 중 2·3의 배수 제외 = 1571113
✅ 증감: i++ (사용 후), ++i (증가 후) / 7/2=3, 7%2=1
✅ a[i] ≡ *(a+i) / 문자열은 \0 종료 / sizeof("ABC" 배열)=4
✅ 값 전달은 원본 불변 → 바꾸려면 포인터 전달
✅ 구조체: . / -> , 크기에 패딩 포함 / union 크기 = 최대 멤버
✅ 재귀 2진수 변환: binary(n/2) → printf(n%2)
✅ 소문자→대문자: ch - 'a' + 'A'
✅ 무한 루프 for(;;) / break로 탈출
✅ malloc(초기화 X) / calloc(0 초기화) / free 후 NULL
✅ 객체지향: 캡슐화·상속·다형성·추상화 / 오버로딩(이름 같고 인자 다름) vs 오버라이딩(재정의)
✅ 컴파일러(전체 번역) vs 인터프리터(한 줄씩 실행) = 스크립트
✅ Python: / 실수, // 정수, 슬라이스 끝 제외 / Shell: 변수 = 양옆 공백 금지, $? 종료 코드

13. 다음 편 예고

다음 편(09편)은 이 시리즈의 마지막 개발자 환경 구축 + 구 범위 보충 입니다.

운영체제 기본 명령어와 작업 우선순위(nice, 스케줄링), 개발도구, 그리고 구 범위에서 반복 출제된 어셈블러·로더·소프트웨어 테스트(블랙박스·화이트박스·순환 복잡도) 까지 한 번에 정리합니다!

 

궁금한 점은 댓글로 남겨주세요!

반응형
개인정보처리방침  |  블로그 소개