정보처리기사 실기에서 프로그래밍 문제는 전체 20문항 중 4~6문항을 차지합니다. C언어, Java, Python 세 가지 언어에서 번갈아 출제되는데요. 코드를 직접 짜는 게 아니라 "이 코드를 실행하면 결과가 뭐야?"를 묻는 코드 추적 문제가 대부분입니다. 언어 자체를 깊이 배울 필요는 없고, 시험에 자주 나오는 패턴만 잡으면 충분히 점수를 챙길 수 있어요.

📌 프로그래밍 출제 비중 & 유형
코드 추적 — 코드 실행 결과 출력값 쓰기 (가장 많음)
빈칸 채우기 — 코드 일부가 비워져 있고 올바른 키워드/값 작성
오류 찾기 — 잘못된 코드 줄 찾아 수정

언어별로 C → 포인터·재귀, Java → 상속·오버라이딩, Python → 리스트·람다가 단골 출제 테마입니다.

C 언어

C언어 문제에서 가장 많이 나오는 건 포인터재귀함수입니다. 이 두 가지만 완벽히 잡아도 C언어 문제의 70%는 풀 수 있어요.

① 기본 출력과 연산자

#include <stdio.h>

int main() {
    int a = 10, b = 3;

    printf("%d\n", a / b);   // 정수 나눗셈 → 3
    printf("%d\n", a % b);   // 나머지 → 1
    printf("%d\n", a++);     // 출력 후 증가 → 10
    printf("%d\n", ++b);     // 증가 후 출력 → 4
    return 0;
}
🔍 코드 추적
a / b → 10 ÷ 3 = 3 (소수점 버림)
a % b → 10 mod 3 = 1
a++ → 현재 a(10) 출력 후 a가 11이 됨 → 10
++b → b를 먼저 4로 만든 뒤 출력 → 4
💡 후위(a++) vs 전위(++a) 헷갈릴 때
후위(a++) — "나 먼저 쓰고 나서 올라갈게" → 현재 값 출력 후 증가
전위(++a) — "올라가고 나서 쓸게" → 먼저 증가 후 출력

② 포인터 — C언어 최빈출 주제

포인터는 변수의 메모리 주소를 저장하는 변수입니다. &는 주소 가져오기, *는 주소가 가리키는 값 가져오기입니다.

#include <stdio.h>

int main() {
    int a = 10;
    int *p = &a;    // p는 a의 주소를 저장

    printf("%d\n", a);   // 10
    printf("%d\n", *p);  // p가 가리키는 값 → 10

    *p = 20;             // p가 가리키는 곳(a)의 값을 20으로 변경
    printf("%d\n", a);   // a도 20으로 바뀜 → 20

    return 0;
}
🔍 메모리 구조로 이해하기
0x100 (a)
10
0x104 (p)
0x100
p는 a의 주소(0x100)를 담고 있고, *p는 그 주소로 가서 값(10)을 가져옵니다.
*p = 20을 하면 0x100 위치(= a)의 값이 20으로 바뀝니다.

③ 배열과 포인터

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *p = arr;   // 배열 이름 = 첫 번째 원소의 주소

    printf("%d\n", *p);       // 10
    printf("%d\n", *(p+1));   // 20  (주소+1 칸 이동)
    printf("%d\n", *(p+2));   // 30
    printf("%d\n", p[3]);     // 40  (p[n] == *(p+n))

    p++;                       // p가 arr[1]을 가리킴
    printf("%d\n", *p);       // 20
    return 0;
}
🎯 배열 포인터 핵심 공식
arr[i] == *(arr + i) == *(p + i) == p[i]
배열 이름(arr)은 그 자체가 첫 번째 원소(&arr[0])의 주소입니다.

④ 재귀 함수 — 두 번째 단골 주제

재귀는 함수가 자기 자신을 호출하는 것입니다. 시험에서는 팩토리얼, 피보나치, 거듭제곱 형태로 자주 나옵니다.

#include <stdio.h>

int factorial(int n) {
    if (n <= 1) return 1;        // 종료 조건 (base case)
    return n * factorial(n - 1); // 자기 자신 호출
}

int main() {
    printf("%d\n", factorial(5)); // ?
    return 0;
}
🔍 호출 스택 추적 (factorial(5))
factorial(5) = 5 × factorial(4)
factorial(4) = 4 × factorial(3)
factorial(3) = 3 × factorial(2)
factorial(2) = 2 × factorial(1)
factorial(1) = 1 ← 여기서 멈춤

↑ 거슬러 올라가며 계산
2×1=2 → 3×2=6 → 4×6=24 → 5×24=120
// 피보나치 수열 (자주 출제!)
int fib(int n) {
    if (n <= 1) return n;
    return fib(n-1) + fib(n-2);
}
// fib(5) = fib(4)+fib(3) = 3+2 = 5
// fib(0)=0, fib(1)=1, fib(2)=1, fib(3)=2, fib(4)=3, fib(5)=5

⑤ 구조체

#include <stdio.h>

struct Student {
    char name[20];
    int  score;
};

int main() {
    struct Student s = {"홍길동", 95};

    printf("%s\n", s.name);   // 홍길동
    printf("%d\n", s.score);  // 95

    struct Student *p = &s;
    printf("%s\n", p->name);  // 포인터로 접근할 때는 ->
    return 0;
}
💡 . vs -> 구분법
구조체 변수로 접근 → s.name (점 연산자)
구조체 포인터로 접근 → p->name (화살표 연산자)

Java

Java 문제에서는 클래스·상속·오버라이딩이 압도적으로 자주 나옵니다. 코드를 보고 어떤 메서드가 실행될지, 출력값이 뭔지를 추적하는 패턴이 반복됩니다.

① 클래스와 생성자

class Animal {
    String name;
    int age;

    // 생성자
    Animal(String name, int age) {
        this.name = name;  // this = 현재 객체
        this.age  = age;
    }

    void speak() {
        System.out.println(name + "이(가) 소리를 냅니다.");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Animal("강아지", 3);
        a.speak();   // 강아지이(가) 소리를 냅니다.
        System.out.println(a.age); // 3
    }
}

② 상속 + 오버라이딩 — 최빈출!

오버라이딩은 부모 클래스의 메서드를 자식 클래스에서 같은 이름으로 재정의하는 것입니다. 실기에서 "출력값은?" 형태로 단골 출제됩니다.

class Animal {
    void speak() {
        System.out.println("...");
    }
}

class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("멍멍!");  // 오버라이딩
    }
}

class Cat extends Animal {
    @Override
    void speak() {
        System.out.println("야옹~");  // 오버라이딩
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();  // 다형성 — Animal 타입으로 Dog 객체 생성
        a.speak();             // ?

        Animal b = new Cat();
        b.speak();             // ?
    }
}
🔍 코드 추적
Animal a = new Dog() — 타입은 Animal이지만 실제 객체는 Dog
a.speak() → 실제 객체(Dog)의 speak() 실행 → 멍멍!
b.speak() → 실제 객체(Cat)의 speak() 실행 → 야옹~

이것이 다형성(Polymorphism)입니다. 변수 타입이 아니라 실제 객체 타입의 메서드가 실행됩니다.

③ 오버로딩 vs 오버라이딩 구분

구분오버로딩(Overloading)오버라이딩(Overriding)
정의같은 이름, 매개변수가 다른 메서드 여러 개부모 메서드를 자식이 재정의
위치같은 클래스 안부모·자식 클래스 간
반환 타입달라도 됨같아야 함
예시add(int a), add(int a, int b)@Override void speak()

④ 인터페이스와 추상 클래스

interface Flyable {
    void fly();           // 구현 없음, 반드시 오버라이딩 필요
}

abstract class Vehicle {
    abstract void move(); // 추상 메서드 — 반드시 오버라이딩
    void stop() {         // 일반 메서드 — 그대로 상속 가능
        System.out.println("정지");
    }
}

class Airplane extends Vehicle implements Flyable {
    @Override
    public void move() { System.out.println("활주로 이동"); }

    @Override
    public void fly()  { System.out.println("비행 중"); }
}

public class Main {
    public static void main(String[] args) {
        Airplane ap = new Airplane();
        ap.move();  // 활주로 이동
        ap.fly();   // 비행 중
        ap.stop();  // 정지  (Vehicle에서 상속)
    }
}
🎯 인터페이스 vs 추상 클래스 차이
인터페이스추상 클래스
다중 구현✅ 여러 개 implements 가능❌ 단일 extends만
일반 메서드default 메서드만 가능✅ 일반 메서드 포함 가능
생성자❌ 없음✅ 있음

⑤ static과 인스턴스 변수

class Counter {
    static int count = 0;  // 모든 객체가 공유
    int id;

    Counter() {
        count++;
        id = count;
    }
}

public class Main {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        Counter c3 = new Counter();

        System.out.println(Counter.count); // ?
        System.out.println(c1.id);         // ?
        System.out.println(c3.id);         // ?
    }
}
🔍 코드 추적
c1 생성: count++ → count=1, c1.id=1
c2 생성: count++ → count=2, c2.id=2
c3 생성: count++ → count=3, c3.id=3

Counter.count3
c1.id1
c3.id3

Python

Python은 문법이 간결해서 비교적 읽기 쉽습니다. 시험에서는 리스트 조작, 슬라이싱, 반복문, 함수가 주로 나와요. 최근에는 람다식과 리스트 컴프리헨션도 출제됩니다.

① 리스트와 슬라이싱

a = [1, 2, 3, 4, 5]

print(a[0])     # 1  (첫 번째)
print(a[-1])    # 5  (마지막)
print(a[1:3])   # [2, 3]  (인덱스 1 이상, 3 미만)
print(a[::2])   # [1, 3, 5]  (2칸씩 건너뜀)
print(a[::-1])  # [5, 4, 3, 2, 1]  (역순)

a.append(6)     # 끝에 추가 → [1,2,3,4,5,6]
a.insert(0, 0)  # 0번 위치에 0 삽입 → [0,1,2,3,4,5,6]
a.pop()         # 마지막 제거 → [0,1,2,3,4,5]
a.remove(3)     # 값 3 제거 → [0,1,2,4,5]
💡 슬라이싱 공식
a[start:end:step]
start부터 end-1까지, step 간격으로 가져옵니다.
생략하면 → start=0, end=끝, step=1

② 반복문과 조건문

# for문 — range
for i in range(1, 6):   # 1, 2, 3, 4, 5
    print(i, end=' ')
# 출력: 1 2 3 4 5

# 리스트 순회
fruits = ['사과', '바나나', '포도']
for fruit in fruits:
    print(fruit)

# enumerate — 인덱스와 값 동시에
for i, fruit in enumerate(fruits):
    print(i, fruit)
# 0 사과
# 1 바나나
# 2 포도

③ 함수와 기본값 매개변수

def greet(name, greeting='안녕하세요'):
    print(f"{greeting}, {name}!")

greet('홍길동')             # 안녕하세요, 홍길동!
greet('이순신', '반갑습니다') # 반갑습니다, 이순신!

④ 람다(lambda)와 map/filter

# 람다 — 이름 없는 함수
square = lambda x: x ** 2
print(square(5))   # 25

# map — 리스트 각 원소에 함수 적용
nums = [1, 2, 3, 4, 5]
result = list(map(lambda x: x * 2, nums))
print(result)   # [2, 4, 6, 8, 10]

# filter — 조건에 맞는 원소만 추출
evens = list(filter(lambda x: x % 2 == 0, nums))
print(evens)    # [2, 4]

⑤ 리스트 컴프리헨션 — 최근 출제 증가

# 기본 형태: [표현식 for 변수 in 이터러블 if 조건]
squares = [x**2 for x in range(1, 6)]
print(squares)   # [1, 4, 9, 16, 25]

# 조건 포함
even_sq = [x**2 for x in range(1, 11) if x % 2 == 0]
print(even_sq)   # [4, 16, 36, 64, 100]
🔍 리스트 컴프리헨션 추적
[x**2 for x in range(1, 6)]
x=1: 1² = 1
x=2: 2² = 4
x=3: 3² = 9
x=4: 4² = 16
x=5: 5² = 25
결과 → [1, 4, 9, 16, 25]

⑥ 딕셔너리

student = {'이름': '홍길동', '성적': 90, '학과': '컴공'}

print(student['이름'])          # 홍길동
print(student.get('학년', 1))  # 키가 없으면 기본값 1 반환

student['학년'] = 3             # 추가
del student['학과']             # 삭제

for key, value in student.items():
    print(f"{key}: {value}")

🧩 실전 문제 풀어보기

📝 문제 1 — C언어
아래 코드의 출력 결과를 쓰시오.

#include <stdio.h>
int func(int n) {
    if (n == 0) return 0;
    return n + func(n - 1);
}
int main() {
    printf("%d\n", func(5));
}
🔍 풀이
func(5) = 5 + func(4)
func(4) = 4 + func(3)
func(3) = 3 + func(2)
func(2) = 2 + func(1)
func(1) = 1 + func(0)
func(0) = 0 ← 종료

1+0=1 → 2+1=3 → 3+3=6 → 4+6=10 → 5+10= 15
📝 문제 2 — Java
아래 코드의 출력 결과를 쓰시오.

class A {
    int x = 10;
    void print() { System.out.println("A: " + x); }
}
class B extends A {
    int x = 20;
    @Override
    void print() { System.out.println("B: " + x); }
}
public class Main {
    public static void main(String[] args) {
        A obj = new B();
        obj.print();
        System.out.println(obj.x);
    }
}
🔍 풀이
A obj = new B() — 타입은 A, 실제 객체는 B
obj.print()메서드는 실제 객체(B) 기준B: 20
obj.x필드는 참조 타입(A) 기준10

⚠ 메서드는 다형성 적용되지만, 필드(변수)는 다형성 적용 안 됨!
📝 문제 3 — Python
아래 코드의 출력 결과를 쓰시오.

data = [3, 1, 4, 1, 5, 9, 2, 6]
result = sorted(set(data), reverse=True)
print(result[:3])
🔍 풀이
set(data) → 중복 제거 → {1, 2, 3, 4, 5, 6, 9}
sorted(..., reverse=True) → 내림차순 → [9, 6, 5, 4, 3, 2, 1]
[:3] → 앞 3개 → [9, 6, 5]
📚 프로그래밍 실기 고득점 전략 3가지

① 손으로 직접 추적하세요. 변수 값이 바뀔 때마다 종이에 표로 적어가며 따라가는 게 가장 확실합니다. 머릿속으로만 계산하면 실수합니다.

② C언어는 포인터·재귀, Java는 상속·다형성에 집중하세요. 이 두 테마가 전체 프로그래밍 문제의 절반 이상을 차지합니다.

③ Python은 슬라이싱 인덱스를 정확히 익히세요. a[1:3]이 인덱스 1~2(3 미포함)라는 것, a[::-1]이 역순이라는 것을 헷갈리면 안 됩니다.