[Day7] C++ Pointer & Reference

베리투스·2025년 8월 12일

TIL: Today I Learned

목록 보기
15/93

C++을 공부하며 반드시 마주치는 두 개의 큰 산, 포인터와 레퍼런스를 만났다. 처음에는 '그냥 변수 쓰면 되지 왜 이렇게 복잡한 게 필요할까?' 싶었지만, 그 이유를 알고 나니 C++의 깊은 매력을 느낄 수 있었다. 단순히 값을 복사하는 것의 비용 문제를 해결하고, 메모리를 효율적으로 관리하기 위해 주소를 직접 다루는 포인터와, 변수에 새로운 이름(별명)을 붙여주는 레퍼런스의 기본 개념을 확실히 정리해 보았다. 🧐


📌 목표

  • 변수의 '값'과 '주소'의 차이를 이해한다.
  • 값 복사에 왜 '비용'이 발생하는지 이해한다.
  • 포인터(Pointer)가 무엇인지, 왜 사용하는지 설명할 수 있다.
  • 레퍼런스(Reference)가 무엇인지, 포인터와 어떻게 다른지 설명할 수 있다.

📖 이론

1. 복사는 비용이 있다 (The Cost of Copying)

int b = a;와 같이 변수를 복사하면, 컴퓨터는 a가 가진 값을 그대로 복사해서 b라는 새로운 메모리 공간에 넣어준다. int(4바이트)처럼 작은 변수는 문제가 없었지만, 만약 수백만 개의 데이터를 가진 배열을 복사한다면 어떨지 생각해 보았다. 😱

이는 마치 4,000장짜리 책을 한 글자씩 전부 베껴 쓰는 것과 같았다. 시간도, 공간도 엄청나게 낭비되는 것이다. C++에서는 이런 비효율을 막기 위해 "값을 통째로 복사하지 말고, 값이 어디에 있는지 '주소'만 알려주자!"라는 아이디어를 사용했다.

2. 포인터(Pointer): 주소를 담는 변수

포인터는 바로 이 '주소'를 저장하기 위해 태어난 특별한 변수이다.

  • 역할: 다른 변수의 메모리 주소 값을 저장한다.
  • 선언: 타입* 변수명; 형태로 선언한다. *는 이 변수가 포인터임을 나타낸다. (예: int* ptr;)
  • 핵심 연산자:
    • & (주소 연산자): 변수 앞에 붙여 그 변수의 메모리 주소를 가져온다. (예: &myVar)
    • * (역참조 연산자): 포인터 변수 앞에 붙여, 그 포인터가 가리키는 주소에 실제로 저장된 값을 가져온다. (예: *ptr)

위 그림처럼, 포인터 변수 ptrx의 값인 3을 직접 담는 게 아니라, x가 위치한 메모리 주소 200을 담고 있다. 그리고 *ptr을 통해 비로소 값 3에 접근할 수 있다.

3. 레퍼런스(Reference): 변수의 또 다른 이름

레퍼런스는 이미 존재하는 변수에 새로운 이름(별명)을 붙여주는 것이다.

  • 역할: 특정 변수를 가리키는 별명 역할을 한다.
  • 선언: 타입& 변수명 = 원본변수; 형태로 선언하며, 선언과 동시에 반드시 초기화해야 한다. (예: int& ref = myVar;)
  • 특징:
    • 한 번 별명이 정해지면 다른 변수의 별명이 될 수 없다. (재할당 불가)
    • 포인터처럼 nullptr을 가질 수 없으며, 반드시 실제 변수를 가리켜야 한다.
    • 사용할 때는 일반 변수처럼 사용하면 된다. *& 같은 복잡한 연산자가 필요 없어 편리하다.

4. 포인터 vs 레퍼런스 (핵심 차이)

구분포인터 (Pointer)레퍼런스 (Reference)
본질주소를 저장하는 독립된 변수기존 변수의 별명(Alias)
초기화선언만 하고 나중에 값을 할당할 수 있음선언과 동시에 반드시 초기화해야 함
재할당가리키는 대상을 다른 변수로 변경 가능한 번 가리킨 대상을 변경할 수 없음
Null 가능성nullptr을 가질 수 있음 (아무것도 가리키지 않음)불가능 (항상 유효한 대상을 가리켜야 함)
사용법* 연산자로 값에 접근 (*ptr)일반 변수처럼 사용 (ref)

💻 코드

포인터와 레퍼런스의 기본 개념 코드

#include <iostream>

int main() {
    // 1. 원본 변수 선언
    int original_value = 10;
    
    // 2. 포인터 선언 및 사용
    int* pointer_var; // 포인터 변수 선언
    pointer_var = &original_value; // original_value의 '주소'를 저장

    // 3. 레퍼런스 선언 및 사용
    // 레퍼런스는 선언과 동시에 반드시 초기화해야 합니다.
    int& reference_var = original_value; 

    // 값 출력해보기
    std::cout << "--- 값 확인 ---" << std::endl;
    std::cout << "원본 값: " << original_value << std::endl;
    std::cout << "포인터를 통해 접근한 값: " << *pointer_var << std::endl;
    std::cout << "레퍼런스를 통해 접근한 값: " << reference_var << std::endl;

    // 주소 값 출력해보기
    std::cout << "\n--- 주소 확인 ---" << std::endl;
    std::cout << "원본 변수의 주소: " << &original_value << std::endl;
    std::cout << "포인터 변수가 저장하고 있는 값(주소): " << pointer_var << std::endl;
    std::cout << "레퍼런스 변수의 주소: " << &reference_var << std::endl;

    return 0;
}

⚠️ 실수

  • 처음에는 포인터 변수에 주소가 아닌 일반 값을 넣으려고 시도했다. int* ptr = 10; 이런 식이었다. 당연히 컴파일러가 화를 냈다. 😅 포인터는 '주소'만 먹는 편식쟁이라는 걸 기억해야 했다.
  • 레퍼런스를 선언만 해놓고 int& ref; 나중에 초기화하려고 했는데, 레퍼런스는 선언과 동시에 짝을 맺어줘야 한다는 규칙을 몰라서 한참 헤맸다. 마치 태어나자마자 운명의 상대를 만나야 하는 것과 같았다. 😂

✅ 핵심 요약

개념설명비고
값 복사변수의 실제 데이터를 복사하는 것. 데이터가 크면 비용이 비싸다.int b = a;
주소데이터가 저장된 메모리의 고유한 위치 값.&a
포인터변수의 '주소'를 값으로 가지는 특별한 변수.int* ptr = &a; , *ptr로 값에 접근
레퍼런스기존 변수에 붙여주는 '별명'.int& ref = a;, ref로 값에 접근 (더 간편함)
profile
Shin Ji Yong // Unreal Engine 5 공부중입니다~

0개의 댓글