좋아요! 🔥 정보처리기사 실기에서 나오는 문제 스타일은 단순한 코드처럼 보여도 함정이나 세심한 부분을 놓치면 틀리는 경우가 많아요.
여기 연습문제 하나를 낼게요.
✅ 문제를 보고 스스로 생각해본 다음, 아래 풀이와 디버깅식 해설을 읽어보세요.
다음 코드를 보고 출력값을 쓰세요.
def func(values):
result = []
for i in range(len(values)):
if i % 2 == 0:
result.append(values[i] * 2)
else:
result.append(values[i - 1] + values[i])
return result
arr = [1, 2, 3, 4, 5, 6]
print(func(arr))
i % 2 == 0 조건: 짝수 인덱스와 홀수 인덱스를 나눠서 처리함.else 부분에서 values[i-1] + values[i]: 현재 인덱스보다 한 칸 앞의 값을 가져오므로 주의해야 함.result.append(values[i - 1] + values[i]) 부분에서 i-1 인덱스 접근 → 음수 아닌지, 0번째에서는 걸리는지 검사해볼 것.입력값: arr = [1, 2, 3, 4, 5, 6]
반복문 돌아가며 상태 추적하기:
| i | 조건 | 처리식 | result 리스트 상태 |
|---|---|---|---|
| 0 | 짝수 → i % 2==0 | values[0] * 2 = 1*2 = 2 | [2] |
| 1 | 홀수 → else | values[0]+values[1] = 1+2 = 3 | [2, 3] |
| 2 | 짝수 | values[2]*2 = 3*2 = 6 | [2, 3, 6] |
| 3 | 홀수 | values[2]+values[3] = 3+4 = 7 | [2, 3, 6, 7] |
| 4 | 짝수 | values[4]*2 = 5*2 = 10 | [2, 3, 6, 7, 10] |
| 5 | 홀수 | values[4]+values[5] = 5+6 = 11 | [2, 3, 6, 7, 10, 11] |
[2, 3, 6, 7, 10, 11]
i가 짝수(0,2,4): 현재 값의 2배를 추가i가 홀수(1,3,5): 이전 값과 현재 값을 합해서 추가i=0일 때 else에 들어가지 않으므로 인덱스 에러 없음values[i-1] 부분에서 인덱스 음수를 잘못 생각하는 경우가 많아요.좋아요! 🎯 이번에는 C 언어 포인터 트릭을 이용한 문제를 준비해 드릴게요. 정보처리기사나 실무 준비하면서 초보자가 틀리기 쉬운 부분을 포함한 함정 문제입니다. 반드시 꼼꼼히 디버깅해보며 풀어보세요!
다음 C 코드를 보고 최종 출력값을 적으세요.
#include <stdio.h>
#include <stdlib.h>
int main() {
int arr[4] = {10, 20, 30, 40};
int *p = arr; // arr[0]의 주소
int **pp = &p; // p의 주소
// 포인터 연산
(*pp)++; // p가 한 칸 증가 -> 이제 arr[1] 가리킴
printf("%d\n", *p); // 출력 1
p = p + 2; // p를 두 칸 증가 -> arr[3] 가리킴
printf("%d\n", *p); // 출력 2
**pp = **pp + 5; // p가 가리키는 값에 +5
printf("%d\n", arr[3]); // 출력 3
return 0;
}
(*pp)++: p 포인터를 한 칸 증가 → arr[1]이 됨.p = p + 2: 현재 p가 arr[1] → arr[3]을 가리키도록 함.**pp = **pp + 5: *pp는 p이므로 현재 p가 가리키는 arr[3]에 +5 함.| 단계 | 코드 | p가 가리키는 위치 | arr 메모리 상태 |
|---|---|---|---|
| 초기 | p = arr | arr[0]=10 | [10,20,30,40] |
| (*pp)++ | p++ | arr[1]=20 | [10,20,30,40] |
| 출력1 | printf("%d\n", *p); | 20 출력 | |
| p = p+2 | p → arr[3]=40 | arr[3]=40 | |
| 출력2 | printf("%d\n", *p); | 40 출력 | |
| **pp += 5 | arr[3]=40+5 | arr[3]=45 | [10,20,30,45] |
| 출력3 | printf("%d\n", arr[3]); | 45 출력 |
20
40
45
(*pp)++는 p 포인터 자체를 증가시키는 것**pp = **pp + 5는 현재 p가 가리키는 값에 연산을 가함++, +2와 같이 메모리 단위 증가라는 점 기억하기!좋아요! 😎 이번엔 포인터 트릭을 한 단계 더 어렵게 준비해 드릴게요.
이번 문제는 **포인터 연산 + 2중 포인터 + 동적 메모리(malloc)**까지 섞여 있어요.
실기에서 이런 문제가 나오면 초보자들은 꼭 헷갈리니 꼼꼼히 보세요!
다음 C 코드를 보고 최종 출력값을 적으세요.
#include <stdio.h>
#include <stdlib.h>
int main() {
// 1. 정수 4개를 위한 동적 메모리 할당
int *arr = (int*)malloc(sizeof(int) * 4);
arr[0] = 5; arr[1] = 10; arr[2] = 15; arr[3] = 20;
// 2. 포인터와 이중포인터
int *p = arr + 1; // arr[1]의 주소 (10을 가리킴)
int **pp = &p;
// 3. 포인터 연산 트릭들
*p += 5; // arr[1] 값 10 → 15
(*pp) += 2; // p를 arr[3]로 옮김
**pp -= 10; // arr[3] 값 20 → 10
// 4. 다른 포인터 트릭
p--; // arr[2]로 되돌아옴
*p *= 2; // arr[2] 값 15 → 30
// 5. 최종 출력
for(int i=0; i<4; i++){
printf("%d ", arr[i]);
}
free(arr);
return 0;
}
이런 문제는 한 줄 한 줄 상태를 적어보며 추적하는 게 좋아요.
중요한 건 p가 어디를 가리키고 있는지, (*pp) 연산이 무엇을 바꾸는지를 아는 것입니다.
초기 상태 (메모리):
arr[0]=5, arr[1]=10, arr[2]=15, arr[3]=20
p = arr + 1 (arr[1]을 가리킴)
pp = &p
| 단계 | 코드 | p가 가리키는 값 | arr 상태 |
|---|---|---|---|
| 초기 | arr[1]=10 | [5,10,15,20] | |
*p += 5 | p가 arr[1]가리킴 → arr[1]=15 | [5,15,15,20] | |
(*pp) += 2 | p += 2 → p가 arr[3]을 가리킴 | [5,15,15,20] | |
**pp -= 10 | arr[3]에서 -10 → arr[3]=10 | [5,15,15,10] | |
p-- | p를 한 칸 감소 → arr[2] 가리킴 | [5,15,15,10] | |
*p *= 2 | arr[2]=15→30 | [5,15,30,10] |
5 15 30 10
(*pp) += 2; → p를 한 칸이 아닌 두 칸 점프**pp -= 10; → 현재 p가 가리키는 값(arr[3])을 수정p-- 후 *p *= 2; → 한 칸 되돌아와 arr[2]를 2배free() 잊지 말기!좋은 질문이에요! ✨
여기서 혼동하기 쉬운 부분인데, 포인터에 괄호((*pp), `pp`)를 씌운다고 옆으로 옮긴다는 뜻은 아니에요.**
괄호의 의미를 정리해 드릴게요.
pp :
pp 자체는 int** 타입, 즉 포인터를 가리키는 포인터pp가 들고 있는 건 p의 주소*pp :
pp를 한 번 역참조(*) → p의 값, 즉 int**pp = p**pp :
pp를 두 번 역참조(**) → p가 가리키고 있는 값(int)**pp = *p(*pp))의 역할포인터 연산을 이해하기 위해 괄호를 쓰는 거예요.
예를 들어,
(*pp) += 2;
이건:
(*pp) → p 포인터 자체를 가져옴+= 2 → p의 값(주소값)을 2 증가“괄호가 있으면 무조건 옆으로(한 칸) 옮긴다?”
괄호((*))의 의미는:
실제 옮겨지는 건 += 2 같은 연산 때문이지, 괄호 때문이 아니에요.
즉:
(*pp) += 2; // p 자체(주소)가 2칸 옮겨지는 거
**pp += 2; // p가 가리키는 값 자체가 2 증가하는 거
✅ 괄호((*pp))의 목적은 연산자 우선순위를 정확히 잡기 위해 쓰이는 거고,
✅ "옆으로 옮긴다"는 += 2 처럼 포인터 산술을 했기 때문이에요.
맞아요! 🎯 아주 좋은 비유예요.
괄호(
())를 씌우면 "우선순위를 잡는다" → 연산자에게 터치 안 받도록 고정시킨다고 이해해도 좋아요.
포인터 연산에서 괄호(())를 쓰는 이유는:
*(역참조 연산자)와 ++, +=(덧셈 연산자)가 같이 있을 때*pp++와 (*pp)++의 차이| 코드 | 의미 |
|---|---|
*pp++ | pp++부터 연산 → pp(포인터 자체)를 증가 후, 증가 전의 pp가 가리키던 대상을 역참조 |
(*pp)++ | *pp부터 연산 → pp가 가리키던 값(p)을 가져와서 증가 (p 자체를 증가) |
**pp++와 (**pp)++| 코드 | 의미 |
|---|---|
**pp++ | pp++ 먼저 → pp 한 칸 증가 후, 증가 전의 값에 ** 역참조 |
(**pp)++ | pp 역참조 → *pp(즉 p)의 값(*p)을 증가 |
좋아요! 😄
포인터와 증감 연산자(++, --)가 같이 나오면 정말 헷갈리기 쉽습니다.
여기서 가장 중요한 것은 C언어의 연산자 우선순위와 괄호예요.
한번에 깔끔히 정리해 드릴게요.
C에서 중요한 우선순위:
우선순위 높은 쪽 → 괄호 > 후위++ > 전위++ > 역참조(*)
*p++ → *(p++)와 같아요. (후위 ++가 *보다 우선)(*p)++ → 괄호 안의 *p가 우선 → 값 증가| 코드 | 의미 | 동작 순서 |
|---|---|---|
*p++ | *(p++) | 현재 p가 가리키는 값 반환 후 p를 1 증가 |
(*p)++ | 괄호 안에서 *p 계산 → 값 증가 | 현재 p가 가리키는 값 반환 후 그 값 1 증가 |
++*p | ++(*p) | 현재 p가 가리키는 값 증가 후 반환 |
++p | p를 한 칸 증가 후 반환 | p 포인터 자체를 옮기고 반환 |
int arr[3] = {10, 20, 30}; int *p = arr;| 코드 | 의미 | 동작 후 출력값 | 동작 후 상태 |
|---|---|---|---|
*p++ | *(p++) | 10 | p → arr[1] |
(*p)++ | (*p)++ | 20 | arr[1]=21 |
*(++p) | *(++p) | 21 | p → arr[2] |
++*p | ++(*p) | 31 | arr[2]=31 |
✅ *p++ → 포인터 p를 증가시키며 이전 위치의 값 반환
✅ (*p)++ → p가 가리키던 값 자체를 증가시키며 이전 값 반환
✅ ++*p → p가 가리키던 값 자체를 먼저 증가 후 반환
✅ *(++p) → 포인터 p를 먼저 증가시키고 새로운 위치의 값 반환
위에거 21이 아니라 30이다
https://chatgpt.com/c/6900ac8c-d210-8324-9a85-dcc46bb71170