C++에서 포인터, 참조, 값 전달 방식의 핵심 이해

메모리를 '서랍장'으로 이해하기

C++ 학습 과정에서 가장 헷갈리는 개념 중 하나는 바로 포인터참조입니다. 이를 쉽게 이해하기 위해 메모리를 일련의 번호가 붙은 서랍장이라고 상상해보세요. 각 변수는 특정 서랍에 저장되며, 그 서랍의 번호가 바로 주소입니다.

기호 '*'와 '&'의 이중적 역할

이 두 기호는 문맥에 따라 완전히 다른 의미를 가지므로 주의가 필요합니다.

'*' - 별표

  • 선언 시 (타입 옆): 포인터 변수임을 선언합니다. 예: double* ptr; → double형 데이터의 주소를 담을 수 있는 포인터.
  • 실행 시 (변수 앞): 역참조(dereferencing)로 해석됩니다. 즉, 포인터가 가리키는 주소 안의 값을 의미합니다. 예: *ptr = 3.14; → ptr이 가리키는 위치에 3.14 저장.

'&' - 앰퍼샌드

  • 실행 시 (변수 앞): 해당 변수의 메모리 주소를 반환합니다. 예: &num → 변수 num의 주소값.
  • 선언 시 (타입 옆): 참조(reference)를 선언합니다. 즉, 기존 변수의 별명(alias)을 만듭니다. 예: int& ref = num; → ref는 num의 또 다른 이름.

함수 인자 전달 방식 비교

함수에 데이터를 전달하는 세 가지 방법을 살펴보고, 각각의 동작 원리를 명확히 구분해보겠습니다.

1. 값 전달 (Pass by Value)

void increase(int val) {
    val += 5;
}

이 방식은 원본의 사본을 함수에 전달합니다. 함수 내부에서 val을 수정해도 실제 인자는 전혀 영향을 받지 않습니다. 대신 복사 과정에서 성능 오버헤드가 발생할 수 있습니다.

2. 포인터 전달 (Pass by Pointer)

void increase(int* ptr) {
    *ptr += 5;
}

여기서는 변수의 주소를 전달하므로, 함수는 포인터를 통해 원본 데이터에 직접 접근할 수 있습니다. 호출 시에는 increase(&original);처럼 주소 연산자를 사용해야 합니다. 유연성은 높지만, NULL 체크와 역참조 실수 등의 위험이 따릅니다.

3. 참조 전달 (Pass by Reference)

void increase(int& ref) {
    ref += 5;
}

이 방식은 마치 원본 변수에 별명을 붙이는 것과 같습니다. 함수 내에서 ref를 수정하면 호출자 측의 원본도 함께 변경됩니다. 문법은 값 전달처럼 간단하면서도, 포인터 전달과 동일한 효율성을 제공합니다.

비교 요약표

방식 예시 원본 변경 여부 비유
값 전달 func(int x) 아니요 문서의 복사본 전달
포인터 전달 func(int* p) 예 (*p 수정 시) 서랍 열쇠 전달
참조 전달 func(int& r) 서랍에 별칭 부여

실습 코드: 주소 출력을 통한 차이 관찰

다음 코드를 실행하면 각 전달 방식에서 변수의 주소가 어떻게 나타나는지 확인할 수 있습니다.

#include <iostream>
using namespace std;

void byValue(int x) {
    cout << "[값 전달] x의 주소: " << &x << ", 값: " << x << endl;
}

void byPointer(int* p) {
    cout << "[포인터 전달] p가 가리키는 주소: " << p << ", 값: " << *p << endl;
}

void byReference(int& r) {
    cout << "[참조 전달] r의 주소: " << &r << ", 값: " << r << endl;
}

int main() {
    int data = 42;
    cout << "--- 원본 data의 주소: " << &data << " ---" << endl << endl;

    byValue(data);
    byPointer(&data);
    byReference(data);

    return 0;
}

결과 해석

  • byValue: 출력된 주소는 maindata와 다릅니다 — 별도의 스택 프레임에 생성된 복사본입니다.
  • byPointer: p가 가리키는 주소는 data의 주소와 동일합니다 — 원본에 대한 직접 접근.
  • byReference: r의 주소 역시 data와 동일합니다 — 단순히 같은 객체에 대한 다른 이름일 뿐입니다.

혼동 방지를 위한 팁

기호의 역할을 판단할 때는 항상 문맥을 보세요:

  • int& r = a; → 타입 옆의 &참조 선언.
  • ptr = &a; → 타입 없이 사용된 &주소 추출.
  • int* p; → 타입 옆의 *포인터 선언.
  • *p = 10; → 단독 사용된 *역참조.

태그: C++ 포인터 참조 값 전달 주소 전달

7월 30일 22:12에 게시됨