포인터 연산·이중 포인터

Jaemyeong Lee·2024년 12월 12일

게임 서버1

목록 보기
30/220

이 Step에서 다루는 것

  • 포인터 연산이 “정수 덧셈”이 아니라 요소 단위 이동인 이유
  • 배열과 포인터가 왜 자주 같이 등장하는지(배열의 첫 원소 주소, ptr[i] 문법)
  • 이중 포인터(T**)가 필요한 대표 상황(포인터 자체를 바꾸기 / 동적 2D)
  • 포인터로 원본을 바꾸는 대표 예제: Swap

학습 목표

  • ptr + 1이 실제로는 “주소 + sizeof(T)”라는 걸 설명할 수 있다.
  • 포인터 연산이 안전한 범위(같은 배열 내부) 에서만 의미가 있다는 걸 말할 수 있다.
  • pp → *pp → **pp 접근을 말로 설명할 수 있다.

포인터 연산 (ptr + 1)

  • 일반 정수: hp + 1은 “값”이 1 증가합니다.
  • 포인터: ptr + 1은 “값”이 아니라 가리키는 위치가 이동합니다.

핵심 규칙:

  • T* ptr에 대해 ptr + 1다음 T 한 칸으로 이동합니다.
  • 즉 주소 관점으로는 “주소 + sizeof(T)” 입니다.

예: int가 4바이트라면, int* 포인터는 보통 4바이트 단위로 이동합니다.

int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr;      // arr[0]을 가리킴
ptr = ptr + 1;       // arr[1]을 가리킴
*ptr = 999;          // arr[1]이 999로 바뀜

매우 중요한 안전 규칙

  • 포인터 연산은 “같은 배열(연속 메모리 블록) 내부”에서만 안전하게 의미가 있습니다.
  • 임의의 숫자를 주소로 캐스팅해서(예: (int*)100) 연산하는 건 정의되지 않은 동작(UB) 이고,
    학습/실무 모두에서 피해야 합니다.

포인터와 배열

  • 배열은 “연속 메모리”이고, 포인터는 “그 연속 메모리의 어디쯤을 가리키는지”를 나타냅니다.
  • int numbers[100]에서 numbers는 대부분의 식(expression)에서 첫 원소 주소로 변환(decay) 됩니다.
    • 그래서 int* ptr = numbers;가 가능합니다.

같은 의미인 3가지 표기

  • numbers[3]
  • *(numbers + 3)
  • ptr[3] (단, ptrnumbers를 가리키고 있을 때)
int numbers[5] = {1, 2, 3, 4, 5};
int* ptr = numbers;

ptr[3] = 666;        // numbers[3]을 666으로 변경
*(ptr + 1) = 777;    // numbers[1]을 777로 변경

배열이 “진짜 포인터”는 아니라는 포인트(중요)

  • 배열 변수 numbers 자체는 “포인터 변수”가 아닙니다. 그래서 아래는 불가능합니다.
    • numbers = numbers + 1; (배열은 대입 대상이 아님)
  • 또한 sizeof(numbers)는 “포인터 크기”가 아니라 배열 전체 크기가 나옵니다.
int numbers[5] = {1, 2, 3, 4, 5};
int* ptr = numbers;

// 같은 대상 접근
numbers[2] = 100;     // (1) 배열 인덱스
*(ptr + 2) = 200;     // (2) 포인터 연산 + 역참조
ptr[2] = 300;         // (3) 포인터 인덱싱

이중 포인터 (int** pp)

이중 포인터는 “포인터를 가리키는 포인터”입니다.

  • int** pp는 “어딘가에 int*가 있다”고 가정하고 접근합니다.
  • 접근 단계:
    • pp : int*의 주소를 담는다
    • *pp : 그 주소로 가서 int*(포인터)를 꺼낸다
    • **pp: 그 포인터가 가리키는 int 값에 접근한다

개념 그림:

pp  ──▶  p  ──▶  a(10)
      (*pp)     (**pp)

대표 사용처:

  • 함수에서 “포인터 변수 자체”를 바꿔야 할 때 (예: int* p = ...;를 함수에서 새 주소로 바꾸기)
  • 동적 2D(포인터 배열)처럼 포인터가 한 겹 더 필요할 때
int a = 10;
int* p = &a;
int** pp = &p;

std::cout << **pp << '\n';  // 10

Swap 함수

void Swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int a = 12, b = 20;
    Swap(&a, &b);
    // a=20, b=12
}
  • 주소를 받아, 역참조로 원본 값을 직접 교환.

체크 질문 (스스로 답해보기)

  • int* ptr에서 ptr + 1이 “주소 + 1”이 아닌 이유는?
  • numbersptr 중에서 “대입 가능(변수)”은 어느 쪽일까?
  • pp, *pp, **pp는 각각 무엇을 의미할까?

profile
李家네_공부방

0개의 댓글