icon

안동민 개발노트

7장 : 포인터와 참조

const 포인터와 포인터 상수


포인터를 실제 코드에서 안전하게 사용하려면 const가 포인터에 붙는 위치에 따라 의미가 어떻게 달라지는지 정확히 이해해야 합니다.

많은 초급자들이 const int*, int* const, const int* const를 헷갈리며, 이 혼동이 버그로 이어지는 경우가 많습니다.

이 장에서는 세 가지 선언을 명확히 구분하고, 함수 매개변수 설계에서 어떤 형태를 선택해야 하는지 실전 기준으로 정리하겠습니다.


const가 가리키는 대상에 붙는 경우 (const int*)

const int* ptr포인터가 가리키는 값을 수정할 수 없다는 의미입니다.

  • 포인터 자체(ptr)는 다른 주소를 가리키도록 변경할 수 있습니다.
  • 하지만 *ptr을 통해 값을 바꾸는 것은 금지됩니다.
pointer to const
#include <iostream>

int main() {
    int a = 10;
    int b = 20;

    const int* ptr = &a; // int const* ptr 와 동일

    // *ptr = 30; // 컴파일 오류: 가리키는 값 수정 불가
    ptr = &b;     // 가능: 포인터가 다른 대상을 가리키는 것은 허용

    std::cout << "*ptr = " << *ptr << "\n"; // 20
}

포인터 자체가 상수인 경우 (int* const)

int* const ptr포인터 자체가 상수라는 뜻입니다.

  • ptr이 한 번 가리킨 주소는 바꿀 수 없습니다.
  • 대신 가리키는 값(*ptr)은 수정할 수 있습니다.
const pointer
#include <iostream>

int main() {
    int a = 10;
    int b = 20;

    int* const ptr = &a;

    *ptr = 30;    // 가능: 대상 값 수정 가능
    // ptr = &b;  // 컴파일 오류: 포인터 자체는 변경 불가

    std::cout << "a = " << a << "\n"; // 30
}

둘 다 상수인 경우 (const int* const)

const int* const ptr는 포인터 자체도 상수, 가리키는 값도 상수입니다.

  • ptr 재지정 불가
  • *ptr 수정 불가
const pointer to const data
#include <iostream>

int main() {
    int value = 42;
    const int* const ptr = &value;

    // *ptr = 100; // 오류
    // ptr = nullptr; // 오류

    std::cout << *ptr << "\n";
}

읽는 요령: 오른쪽에서 왼쪽으로

복잡한 선언은 변수 이름 기준으로 오른쪽에서 왼쪽으로 읽으면 해석이 쉬워집니다.

  • const int* p -> p는 포인터, 가리키는 int는 const
  • int* const p -> p는 const 포인터
  • const int* const p -> const 포인터가 const int를 가리킴

함수 매개변수 설계에서의 선택 기준

포인터를 매개변수로 받을 때는 “함수가 무엇을 바꿀 수 있어야 하는가?”를 기준으로 결정합니다.

데이터를 읽기만 할 때

const T* 또는 const T&를 사용합니다.

double getLength(const Vector2* v);
double getLength(const Vector2& v);
데이터를 수정해야 할 때

T* 또는 T&를 사용합니다.

void normalize(Vector2* v);
void normalize(Vector2& v);
포인터 자체 재지정까지 막고 싶을 때

함수 내부 구현 관점에서 T* const를 사용할 수 있습니다.

void updateValue(int* const p) {
    // p = nullptr; // 금지
    *p = 99;       // 허용
}

실무에서는 API 사용자 입장에서 명확한 의도를 전달하기 위해 읽기 전용은 const, 수정 가능은 non-const를 엄격히 구분하는 것이 중요합니다.


포인터와 참조 중 무엇을 쓸까?

둘 다 원본 객체를 다룰 수 있지만, 용도가 다릅니다.

  • 참조(T&, const T&): 반드시 유효한 객체가 있어야 하며, null 개념이 없습니다.
  • 포인터(T*, const T*): null(nullptr)을 표현할 수 있어 “없음” 상태를 전달하기 쉽습니다.

즉, “항상 대상이 존재한다”면 참조가 더 단순하고, “대상이 없을 수도 있다”면 포인터가 더 적절합니다.


포인터/참조 선택 기준 정리

  • const int* : 대상 값 수정 금지
  • int* const : 포인터 재지정 금지
  • const int* const : 둘 다 금지
  • 함수 설계에서는 const를 통해 읽기/수정 의도를 명확히 드러내는 것이 핵심입니다.

목차