C++ 초급 - 5. 포인터와 참조 (Pointers and References) (3 - 참조 (& 연산자))

2025. 2. 12. 18:49프로그래밍 언어/C++

📌 5.3 참조 (& 연산자)

참조(Reference)변수의 별칭(Alias) 역할을 하는 변수이다.
참조를 사용하면 기존 변수를 새로운 이름으로 다룰 수 있으며, 포인터와 유사하지만 한 번 초기화되면 다른 변수를 가리킬 수 없는 특징이 있다.

참조는 특히 함수 매개변수 전달(Call by Reference)에서 자주 사용되며, 복사 비용을 줄이고 원본 값을 직접 수정할 수 있도록 해준다.


📌 1. 참조 변수의 선언 및 초기화

🔹 (1) 참조 변수 선언 방식

참조 변수를 선언할 때는 & 연산자를 사용하며, 반드시 초기화해야 한다.

💡 기본 문법

데이터타입 &참조변수명 = 기존변수명;

💡 예제: 참조 변수 선언 및 초기화

#include <iostream>

int main() {
    int num = 10;
    int& ref = num;  // num을 참조하는 ref 선언

    std::cout << "num: " << num << std::endl;
    std::cout << "ref: " << ref << std::endl;

    ref = 20;  // ref를 변경하면 num도 변경됨
    std::cout << "num 변경 후: " << num << std::endl;

    return 0;
}

🔹 출력 결과

num: 10
ref: 10
num 변경 후: 20

💡 설명

  • int& ref = num; → ref는 num의 참조자(별칭).
  • ref = 20; → ref를 변경하면 num도 변경됨.
  • 참조는 기존 변수와 메모리 주소를 공유한다.

주의

  • 참조 변수는 반드시 선언 시 초기화해야 한다.
int& ref;  // ❌ 오류 발생 (초기화되지 않음)
  • 참조 변수는 다른 변수로 변경할 수 없다.
int a = 10, b = 20;
int& ref = a;
ref = b;  // ✅ 변수 값만 변경됨 (참조 대상 변경 ❌)

📌 2. 포인터와 참조의 차이점

참조(Reference)와 포인터(Pointer)는 둘 다 메모리 주소를 활용하지만, 사용 방식이 다르다.

비교 항목 포인터 (*) 참조 (&)
메모리 주소 저장 변수의 주소 저장 가능 변수를 직접 가리킴 (별칭)
초기화 여부 나중에 초기화 가능 선언 시 반드시 초기화
NULL 가능 여부 nullptr 가능 NULL 참조 불가
참조 변경 가능 여부 다른 변수의 주소 저장 가능 한 번 초기화되면 다른 변수로 변경 불가
역참조 방식 *ptr 사용 ref 자체가 변수처럼 동작

💡 포인터와 참조 비교 예제

#include <iostream>

int main() {
    int num = 10;
    int* ptr = &num;  // 포인터
    int& ref = num;   // 참조

    std::cout << "포인터: " << *ptr << ", 참조: " << ref << std::endl;

    *ptr = 20;  // 포인터로 값 변경
    std::cout << "포인터로 변경 후 num: " << num << std::endl;

    ref = 30;  // 참조로 값 변경
    std::cout << "참조로 변경 후 num: " << num << std::endl;

    return 0;
}

🔹 출력 결과

포인터: 10, 참조: 10
포인터로 변경 후 num: 20
참조로 변경 후 num: 30

💡 설명

  • 포인터(ptr)와 참조(ref) 모두 num을 가리킨다.
  • *ptr = 20; → 포인터를 통해 값을 변경 가능.
  • ref = 30; → 참조를 통해 값을 변경 가능.

📌 3. 함수 매개변수에서 참조의 활용 (Call by Reference)

함수에서 매개변수를 전달할 때 값을 복사하는 대신 참조를 사용하면, 원본 값을 직접 수정 가능하다.
이 방식은 복사 오버헤드(Copy Overhead)를 줄이고, 성능을 향상시킬 수 있다.

🔹 (1) 값 전달 (Call by Value)

  • 매개변수를 복사하여 함수 내부에서 사용하므로, 원본 값은 변경되지 않음.

💡 예제: 값 전달

#include <iostream>

void modifyValue(int x) {
    x = 100;  // x의 값 변경 (원본에는 영향 없음)
}

int main() {
    int num = 10;
    modifyValue(num);
    std::cout << "값 전달 후 num: " << num << std::endl;  // 10 (변경 없음)
    return 0;
}

🔹 출력 결과

값 전달 후 num: 10

💡 설명

  • modifyValue(int x)는 num을 복사해서 사용하므로 원본 값은 변경되지 않음.

🔹 (2) 참조 전달 (Call by Reference)

  • 참조를 사용하면 함수 내에서 원본 값을 직접 변경 가능.
  • 복사 비용이 발생하지 않으므로 성능이 향상됨.

💡 예제: 참조 전달

#include <iostream>

void modifyValue(int& x) {  // 참조 매개변수
    x = 100;  // 원본 값 변경
}

int main() {
    int num = 10;
    modifyValue(num);
    std::cout << "참조 전달 후 num: " << num << std::endl;  // 100 (변경됨)
    return 0;
}

🔹 출력 결과

참조 전달 후 num: 100

💡 설명

  • modifyValue(int& x)는 num을 참조하므로, 원본 값이 직접 변경됨.

🔹 (3) 참조를 사용한 const 매개변수

  • 원본 데이터를 변경할 필요는 없지만, 복사 비용을 줄이고 싶을 때 const를 사용한다.

💡 예제: const 참조 사용

#include <iostream>

void printValue(const int& x) {  // x는 변경 불가능
    std::cout << "값: " << x << std::endl;
}

int main() {
    int num = 10;
    printValue(num);
    return 0;
}

🔹 출력 결과

값: 10

💡 장점

  • 함수의 인자로 대용량 데이터(객체, 구조체)를 전달할 때 복사 비용 절감.
  • 원본 데이터의 변경을 방지하여 코드의 안정성을 향상.

📌 4. 정리

개념 설명
참조 (&) 변수의 별칭(Alias) 역할을 하며, 한 번 초기화되면 변경 불가능
포인터와 차이점 포인터는 주소를 저장하며 변경 가능, 참조는 한 번 연결되면 고정
함수에서 활용 참조 전달(Call by Reference)로 복사 오버헤드 감소
const 참조 원본 데이터 변경을 방지하면서 복사 비용을 절감