연습5(주의)-포이터 공부더 필요

류한선·2025년 6월 26일

실기연습-2

목록 보기
45/95

좋아요! 🔥 정보처리기사 실기에서 나오는 문제 스타일은 단순한 코드처럼 보여도 함정이나 세심한 부분을 놓치면 틀리는 경우가 많아요.
여기 연습문제 하나를 낼게요.
✅ 문제를 보고 스스로 생각해본 다음, 아래 풀이와 디버깅식 해설을 읽어보세요.


🎯 연습 문제 1: 파이썬 코드 (실기 스타일 함정 포함)

다음 코드를 보고 출력값을 쓰세요.

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))

🤔 문제의 함정 포인트

  1. i % 2 == 0 조건: 짝수 인덱스와 홀수 인덱스를 나눠서 처리함.
  2. else 부분에서 values[i-1] + values[i]: 현재 인덱스보다 한 칸 앞의 값을 가져오므로 주의해야 함.
  3. result.append(values[i - 1] + values[i]) 부분에서 i-1 인덱스 접근 → 음수 아닌지, 0번째에서는 걸리는지 검사해볼 것.

🐣 디버깅하면서 한 줄씩 추적하기 (값 추적)

입력값: arr = [1, 2, 3, 4, 5, 6]

반복문 돌아가며 상태 추적하기:

i조건처리식result 리스트 상태
0짝수 → i % 2==0values[0] * 2 = 1*2 = 2[2]
1홀수 → elsevalues[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 언어 포인터 트릭을 이용한 문제를 준비해 드릴게요. 정보처리기사나 실무 준비하면서 초보자가 틀리기 쉬운 부분을 포함한 함정 문제입니다. 반드시 꼼꼼히 디버깅해보며 풀어보세요!


🧠 연습 문제 2: 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;
}

🤔 문제 함정 포인트

  1. (*pp)++: p 포인터를 한 칸 증가 → arr[1]이 됨.
  2. p = p + 2: 현재 p가 arr[1] → arr[3]을 가리키도록 함.
  3. **pp = **pp + 5: *ppp이므로 현재 p가 가리키는 arr[3]에 +5 함.

🧠 디버깅 및 한 줄씩 상태 추적하기

단계코드p가 가리키는 위치arr 메모리 상태
초기p = arrarr[0]=10[10,20,30,40]
(*pp)++p++arr[1]=20[10,20,30,40]
출력1printf("%d\n", *p);20 출력
p = p+2p → arr[3]=40arr[3]=40
출력2printf("%d\n", *p);40 출력
**pp += 5arr[3]=40+5arr[3]=45[10,20,30,45]
출력3printf("%d\n", arr[3]);45 출력

🎯 정답 출력:

20
40
45

💡 요약 및 팁

  • (*pp)++p 포인터 자체를 증가시키는 것
  • **pp = **pp + 5는 현재 p가 가리키는 값에 연산을 가함
  • 포인터 연산 시 ++, +2와 같이 메모리 단위 증가라는 점 기억하기!

좋아요! 😎 이번엔 포인터 트릭을 한 단계 더 어렵게 준비해 드릴게요.
이번 문제는 **포인터 연산 + 2중 포인터 + 동적 메모리(malloc)**까지 섞여 있어요.
실기에서 이런 문제가 나오면 초보자들은 꼭 헷갈리니 꼼꼼히 보세요!


🎯 연습 문제 3: C언어 고급 포인터 트릭 (난이도 업!)

다음 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 += 5p가 arr[1]가리킴 → arr[1]=15[5,15,15,20]
(*pp) += 2p += 2 → p가 arr[3]을 가리킴[5,15,15,20]
**pp -= 10arr[3]에서 -10 → arr[3]=10[5,15,15,10]
p--p를 한 칸 감소 → arr[2] 가리킴[5,15,15,10]
*p *= 2arr[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`)를 씌운다고 옆으로 옮긴다는 뜻은 아니에요.**
괄호의 의미를 정리해 드릴게요.


🧠 포인터 괄호의 의미 정리

  1. pp :

    • pp 자체는 int** 타입, 즉 포인터를 가리키는 포인터
    • 메모리 안에서 pp가 들고 있는 건 p의 주소
  2. *pp :

    • pp를 한 번 역참조(*) → p의 값, 즉 int*
    • 쉽게 말해 *pp = p
  3. **pp :

    • pp를 두 번 역참조(**) → p가 가리키고 있는 값(int)
    • **pp = *p

💡 괄호((*pp))의 역할

포인터 연산을 이해하기 위해 괄호를 쓰는 거예요.

예를 들어,

(*pp) += 2;

이건:

  • (*pp)p 포인터 자체를 가져옴
  • += 2p의 값(주소값)을 2 증가

🧠 혼동될 수 있는 부분 정리

잘못된 이해:

“괄호가 있으면 무조건 옆으로(한 칸) 옮긴다?”

정리:

괄호((*))의 의미는:

  • 포인터를 역참조하거나
  • 연산자 우선순위를 조정하기 위함

실제 옮겨지는 건 += 2 같은 연산 때문이지, 괄호 때문이 아니에요.
즉:

(*pp) += 2; // p 자체(주소)가 2칸 옮겨지는 거
**pp += 2;  // p가 가리키는 값 자체가 2 증가하는 거

🎯 한마디 요약

✅ 괄호((*pp))의 목적은 연산자 우선순위를 정확히 잡기 위해 쓰이는 거고,
✅ "옆으로 옮긴다"는 += 2 처럼 포인터 산술을 했기 때문이에요.


맞아요! 🎯 아주 좋은 비유예요.

괄호(())를 씌우면 "우선순위를 잡는다" → 연산자에게 터치 안 받도록 고정시킨다고 이해해도 좋아요.


✏️ 좀 더 쉬운 말로 정리해 볼까요?

포인터 연산에서 괄호(())를 쓰는 이유는:

  1. *(역참조 연산자)와 ++, +=(덧셈 연산자)가 같이 있을 때
  2. 우리가 원하는 대로 묶어주기 위해
    → 괄호로 묶지 않으면 C 언어의 우선순위 규칙에 따라 연산 순서가 바뀌어요.

예시 1: *pp++(*pp)++의 차이

코드의미
*pp++pp++부터 연산 → pp(포인터 자체)를 증가 후, 증가 전의 pp가 가리키던 대상을 역참조
(*pp)++*pp부터 연산 → pp가 가리키던 값(p)을 가져와서 증가 (p 자체를 증가)

예시 2: **pp++(**pp)++

코드의미
**pp++pp++ 먼저 → pp 한 칸 증가 후, 증가 전의 값에 ** 역참조
(**pp)++pp 역참조 → *pp(즉 p)의 값(*p)을 증가

좋아요! 😄
포인터와 증감 연산자(++, --)가 같이 나오면 정말 헷갈리기 쉽습니다.
여기서 가장 중요한 것은 C언어의 연산자 우선순위괄호예요.
한번에 깔끔히 정리해 드릴게요.


🎯 핵심 개념 정리

1️⃣ 연산자 우선순위

C에서 중요한 우선순위:

우선순위 높은 쪽 → 괄호 > 후위++ > 전위++ > 역참조(*)
  • *p++*(p++)와 같아요. (후위 ++가 *보다 우선)
  • (*p)++ → 괄호 안의 *p가 우선 → 값 증가

2️⃣ 괄호 유무로 완전히 달라지는 의미

코드의미동작 순서
*p++*(p++)현재 p가 가리키는 값 반환 후 p를 1 증가
(*p)++괄호 안에서 *p 계산 → 값 증가현재 p가 가리키는 값 반환 후 그 값 1 증가
++*p++(*p)현재 p가 가리키는 값 증가 후 반환
++pp를 한 칸 증가 후 반환p 포인터 자체를 옮기고 반환

3️⃣ 한 번에 보는 예제들

🧠 int arr[3] = {10, 20, 30}; int *p = arr;

코드의미동작 후 출력값동작 후 상태
*p++*(p++)10p → arr[1]
(*p)++(*p)++20arr[1]=21
*(++p)*(++p)21p → arr[2]
++*p++(*p)31arr[2]=31

🎯 한 줄 요약

*p++ → 포인터 p를 증가시키며 이전 위치의 값 반환
(*p)++ → p가 가리키던 값 자체를 증가시키며 이전 값 반환
++*p → p가 가리키던 값 자체를 먼저 증가 후 반환
*(++p) → 포인터 p를 먼저 증가시키고 새로운 위치의 값 반환


위에거 21이 아니라 30이다
https://chatgpt.com/c/6900ac8c-d210-8324-9a85-dcc46bb71170

0개의 댓글