좋아요! 🎯
정보처리기사 실기에서 나오는 문제 중 간단하지만 함정이 있는 코드 문제를 연습용으로 준비해 볼게요.
이번 문제의 컨셉은 **"자료형, 연산자 우선순위, 증감 연산자 트릭"**을 섞어서 준비해봅니다.
다음 Java 코드를 보고 프로그램의 출력값을 적으시오.
(포인트: 연산자 우선순위, 후치 증가, 전치 증가)
class Main {
public static void main(String[] args) {
int a = 10;
int b = 5;
int c = a++ + ++b * 2 - --a + b-- + a++ / b;
System.out.println(c);
System.out.println(a + " " + b);
}
}
코드 상태:
int a = 10; // a=10
int b = 5; // b=5
// 계산식: c = a++ + ++b * 2 - --a + b-- + a++ / b;
a++a 값을 먼저 쓰고, 연산 끝나면 증가10 반환 후, a → 11로 증가++b++b * 2 → 6 * 2 = 12--a- --a 부분 → -10b--a++ / b10 / 5 = 2이제 위 값들을 순서대로 대입합니다.
식:
c = (a++=10) + (++b * 2=12) - (--a=10) + (b--=6) + (a++/b=2)
c = 10 + 12 - 10 + 6 + 2
c = (10+12)=22; 22-10=12; 12+6=18; 18+2=20
a 최종값? 위 계산 중 a++가 두 번 있었죠.
b 최종값? ++b에서 b=6, b--에서 b=5
c = 20
a b = 11 5
이 문제의 함정은 다음과 같아요:
✅ 후치 증가와 전치 증가의 동작 타이밍
✅ 연산자 우선순위 (++b * 2 먼저 계산됨)
✅ 같은 변수를 한 줄에서 여러 번 증감할 때의 상태 추적
좋아요! 이번에는 포인터와 메모리 참조에서 틀리기 딱 좋은 C언어 문제를 하나 준비해 드릴게요.
이런 문제는 주소 계산과 포인터 연산자, * 연산자 우선순위 때문에 많이 헷갈립니다.
다음 코드를 보고 출력값을 예측해보세요.
#include <stdio.h>
int main() {
int arr[3] = {10, 20, 30};
int *p = arr;
int **pp = &p;
printf("%d\n", *p++);
printf("%d\n", (*p)++);
printf("%d\n", *++p);
printf("%d\n", **pp);
return 0;
}
이 문제의 함정 포인트:
*p++ vs (*p)++ → 연산자 우선순위++p와 p++의 차이pp를 이용한 이중 포인터 참조int arr[3] = {10, 20, 30};
int *p = arr; // p → arr[0] (값 10)
int **pp = &p; // pp → p
printf("%d\n", *p++);*p++은 *(p++)와 동일 (후치 증가 우선순위)*p++ → *(p++) → 현재 *p 값(10)을 출력10printf("%d\n", (*p)++);(*p)++ → 현재 p가 가리키는 값 20 반환 후, arr[1]의 값을 1 증가20printf("%d\n", *++p);*++p → *(++p)와 동일30printf("%d\n", **pp);3010
20
30
30
좋아요! 🎯 이번에는 구조체 포인터 트릭, 함수 포인터 트릭, 문자(char) 포인터 트릭 세 가지를 준비해 드릴게요.
각 문제마다 코드와 함정 포인트, 그리고 해설까지 한 줄 한 줄 꼼꼼히 적어드릴게요.
다음 C 코드를 보고 출력값을 예상해보세요.
#include <stdio.h>
struct Point {
int x;
int y;
};
int main() {
struct Point p = {10, 20};
struct Point *ptr = &p;
printf("%d\n", ptr->x++);
printf("%d\n", ++ptr->y);
printf("%d\n", p.x + p.y);
return 0;
}
printf("%d\n", ptr->x++);
ptr->x++ → 현재 x(10) 출력 후 p.x를 11로 증가printf("%d\n", ++ptr->y);
printf("%d\n", p.x + p.y);
10
21
32
다음 코드를 보고 출력값을 예상해보세요.
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int mul(int a, int b) {
return a * b;
}
int main() {
int (*func)(int, int); // 함수 포인터 선언
func = add;
printf("%d\n", func(2, 3));
func = mul;
printf("%d\n", (*func)(2, 3));
return 0;
}
func = add; → func가 add를 가리킴func(2,3) 호출 → add(2,3) → 2+3=5 출력func = mul; → func가 mul을 가리킴(*func)(2,3) 호출 → mul(2,3) → 2*3=6 출력5
6
다음 코드를 보고 출력값을 예상해보세요.
#include <stdio.h>
int main() {
char str[] = "HELLO";
char *p = str;
printf("%c\n", *p++);
printf("%c\n", *++p);
printf("%c\n", *(p+1));
printf("%c\n", p[-1]);
return 0;
}
printf("%c\n", *p++);
*(p++) → 현재 *p='H' 출력 후 p→str[1]printf("%c\n", *++p);
*(++p) → p→str[2], *p='L' 출력printf("%c\n", *(p+1));
printf("%c\n", p[-1]);
H
L
L
E
좋아요! 🎯 이번에는 더 어려운 트릭 문제들을 준비해 드릴게요.
이번엔 비트 연산 트릭과 동적 메모리 트릭 문제를 각 1개씩 준비합니다.
이번에도 문제 코드 + 함정 포인트 + 자세한 풀이까지 한 줄 한 줄 설명해 드릴 거예요.
실전에선 이런 문제에서 오답 나기 쉽습니다. 꼭 따라가며 연습해 보세요!
다음 C 코드를 보고 출력값을 예상해보세요.
#include <stdio.h>
int main() {
unsigned char a = 0xF0; // 11110000
unsigned char b = 0x0F; // 00001111
unsigned char c = a & b;
unsigned char d = a | b;
unsigned char e = a ^ b;
unsigned char f = ~a;
printf("%02X\n", c);
printf("%02X\n", d);
printf("%02X\n", e);
printf("%02X\n", f);
return 0;
}
a = 0xF0 = 11110000(2)b = 0x0F = 00001111(2)c = a & b: 비트 AND
11110000 & 00001111 = 00000000(2) = 0x00d = a | b: 비트 OR
11110000 | 00001111 = 11111111(2) = 0xFFe = a ^ b: 비트 XOR
11110000 ^ 00001111 = 11111111(2) = 0xFFf = ~a: 비트 NOT
~11110000(2) = 00001111(2) = 0x0F00
FF
FF
0F
다음 C 코드를 보고 출력값을 예상해보세요.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int*)malloc(3 * sizeof(int));
p[0] = 5;
p[1] = 10;
p[2] = 15;
int *q = p;
free(p);
printf("%d\n", q[1]);
q[1] = 20;
printf("%d\n", q[1]);
return 0;
}
p에 정수 3개 동적 메모리 할당 → [5, 10, 15]q = p; → q와 p가 같은 메모리 참조free(p); 호출 → p의 메모리는 반환되었지만 q도 같은 주소를 보고 있음.q[1]을 읽거나 쓰는 것은 **정의되지 않은 동작(UB)**이므로 무슨 값이 출력될지 알 수 없음.free(p); 했으므로 q를 쓰면 안 됩니다. 반드시 q=NULL; 처리를 하거나 새 메모리를 할당해야 함.이 문제의 정답은 “정의되지 않은 동작(Undefined Behavior)”입니다.
단순히 10 또는 20이 나올 수 있지만, 믿어선 안 되고 프로그램마다 다릅니다.
이런 문제에서 중요한 건 포인터가 해제된 후 사용되는 경우 위험함을 인지하는 것입니다.
좋아요! 🎯 이번에는 더 고급 트릭 문제들 준비해 드릴게요.
이번엔 구조체 안에 포인터 트릭, 비트필드 트릭, 함수 포인터 배열 트릭 3가지를 연습해봅시다.
다음 C 코드를 보고 출력값을 예상해 보세요.
#include <stdio.h>
struct Data {
char *msg;
};
int main() {
char str1[] = "Hello";
char str2[] = "World";
struct Data d1 = {str1};
struct Data d2 = {str2};
d2 = d1; // 구조체 대입
d1.msg[0] = 'J'; // str1[0] = 'J'
printf("%s\n", d2.msg);
return 0;
}
struct Data d1 = {str1}; → d1.msg → "Hello"struct Data d2 = {str2}; → d2.msg → "World"d2 = d1; → 구조체 대입 → d2.msg = d1.msg → 이제 d2.msg가 str1을 참조d1.msg[0] = 'J'; → str1[0] = 'J' → str1 = "Jello"Jello
다음 C 코드를 보고 출력값을 예상해 보세요.
#include <stdio.h>
struct BitData {
unsigned int a : 3;
unsigned int b : 5;
};
int main() {
struct BitData bd;
bd.a = 7; // 3비트, 최대값 7(111b)
bd.b = 32; // 5비트, 32(100000b)지만 5비트니 모드 연산됨
printf("%u %u\n", bd.a, bd.b);
return 0;
}
bd.a = 7; → 3비트 필드 최대값은 7 → a=7
bd.b = 32; → 5비트 필드(0~31까지 표현 가능)
32 % 2^5 = 32 % 32 = 0 → b=0출력값: 7 0
7 0
다음 C 코드를 보고 출력값을 예상해 보세요.
#include <stdio.h>
int add(int x, int y) { return x + y; }
int sub(int x, int y) { return x - y; }
int mul(int x, int y) { return x * y; }
int main() {
int (*ops[3])(int, int) = { add, sub, mul };
int a = 10, b = 3;
printf("%d\n", ops[2](a, b));
printf("%d\n", (*ops[1])(a, b));
printf("%d\n", (**ops)(a, b));
return 0;
}
ops[0] = add, ops[1] = sub, ops[2] = mulops[2](a,b) = mul(10,3) = 30(*ops[1])(a,b) = sub(10,3) = 7(**ops)(a,b) = add(10,3) = 13ops 자체가 배열의 시작주소이므로 *ops == ops[0] == add → **ops == add)30
7
13
좋아요! 🔍 비트필드 트릭 문제를 아주 자세히 디버깅해 드릴게요.
이 부분에서 헷갈리신 거라면, 비트필드의 크기와 값 저장 방식을 한 줄 한 줄 디버깅하는 방식으로 확실히 정리해봅시다.
#include <stdio.h>
struct BitData {
unsigned int a : 3; // a를 3비트로 저장
unsigned int b : 5; // b를 5비트로 저장
};
int main() {
struct BitData bd;
bd.a = 7; // 3비트 필드
bd.b = 32; // 5비트 필드
printf("%u %u\n", bd.a, bd.b);
return 0;
}
unsigned int a : 3;
→a는 3비트 크기로 제한된 필드
unsigned int b : 5;
→b는 5비트 크기로 제한된 필드
여기서 중요한 점:
✅ 비트필드에 값을 대입하면 지정된 비트 수만 남아요.
✅ 값이 비트를 초과하면 나머지는 버려지고, 2^n으로 나눈 나머지가 남아요.
bd.a = 7;0~7 (2^3 - 1)111 (3비트)bd.b = 32;0~31 (2^5 - 1)100000 (6비트 짜리 수)11111)32 % 2^5 = 32 % 32 = 0✅ 비트필드 내부 동작:
필드에 값이 대입될 때 비트 크기만큼 잘리므로
32 (100000₂)에서 상위 비트를 버리고00000₂(0)만 남아요.
→ 출력:
7 0
비트필드에 값을 대입할 때 이렇게 잘리는 이유는 C 언어 표준에서 비트필드 대입 시 지정된 비트 수만큼만 메모리에 남긴다고 규정하기 때문이에요.
이건 하드웨어 레지스터 같은 곳에 한정된 비트를 쓰도록 설계할 때 유용하지만,
일반 정수 대입할 때는 오버플로우(잘림)가 발생할 수 있다는 점을 꼭 기억하세요.