본문 바로가기
카테고리 없음

포인터에서 참조란 무엇인가?

by 가보자곳 2025. 6. 7.
반응형

참조(reference)는 객체의 또 다른 이름(별칭)

참조 변수 reference variable 는 일반적인 변수와 다르게 메모리 위에 새로운 영역을 잡지 않음

단순하게 기존에 있던 변수와 연결(바인드)해서, 기존에 있던 변수에 새로운 이름을 붙여주는 것 뿐

따라서 원본 변수가 있어야만 생성할 수 있음

 

다음은 int 자료형의 변수 score를 만들고, 이를 기반으로 참조 자료형의 변수 rScore를 만든 뒤 연결하는 코드

int score = 92;            // score 변수 선언과 동시에 92로 초기화
int& rScore = score;       // rScore를 score에 참조로 바인딩 (참조자 선언)

 

 

참조 변수가 선언되고 어떤 변수와 연결되면, 참조 관계는 스코프를 벗어나서 변수가 파괴되기 전까지는 계속해서 유지

즉 참조 관계는 한 번 정의되면 변경할 수 없는 영구적인 관계이다

 

그래서 다음 코드는 오류가 난다

int score = 92;
int& rScore = score;       // rScore를 score에 참조로 바인딩
int num = 80;
int& rScore = num;         // 오류: rScore는 이미 score을 참조하고 있으므로,
                           // 참조 관계를 끊고 다른 변수(num)에 다시 바인딩할 수 없음

 

하지만 하나의 변수를 기반으로 여러 개의 참조 변수를 만들 수 있다.

이를 참조 다중성이라 한다

int num = 100;
int& rNum1 = num; // rNum1은 num에 참조로 바인딩됨
int& rNum2 = num; // rNum2도 num에 참조로 바인딩됨
int& rNum3 = num; // rNum3도 num에 참조로 바인딩됨

 

참고로 참조 변수에 단순한 값을 연결하는 것은 불가능

int& x = 92; // 오류: 리터럴(값)에는 참조를 바인딩할 수 없음

 

 

참조 관계가 만들어지면 데이터 변수 또는 참조 변수를 사용해서 원본 값을 변경할 수 있지만

원본 변수에 const 한정자가 붙으면 값을 변경할 수 없다

 

경우 데이터 변수 참조 변수 상태
1 int name = value; int& rName = name; 가능
2 const int name = value; int& rName = name; 오류
3 int name = value; const int& rName = name; 가능
4 const int name = value; const int& rName = name; 가능

 

경우 1

원본 변수와 참조 변수 중에서 아무 것이나 사용해도 메모리 위치의 값을 변경할 수 있다

 

경우 2

원본 변수가 const 한정자로 고정되어 있으므로

참조 변수를 사용해서 값을 변경할 수 있는 방법이 없음

 

경우 3

원본 변수를 사용해서 값을 변경할 수 있지만,

참조 변수를 사용해서는 값을 변경 할 수 없음

 

경우 4

원본 변수와 참조 변수 모두 값을 확인할 때만 사용할 수 있고,

값을 변경할 수는 없음

이를 활용해서 프로그램을 만드는 경우는 거의 없음

 

 

참조를 활용해서 데이터를 전달하는 것을 참조로 전달(pass-by-reference)

참조를 활용해서 데이터를 리턴하는 것을 참조로 리턴(return-by-reference)

 

 

참조로 전달

값으로 전달에서는 인수 argument 와 매개변수 parameter 가 독립적으로 존재

따라서 매개변수를 변경하는 일이 인수를 변경하는 일에 영향을 주지 않음

값으로 전달에서 문제가 되는 또 다른 부분은 복사 비용

기본 자료형 등의 크기가 작은 자료형을 복사할 때는 큰 문제가 없지만,

클래스 자료 형처럼 크기가 큰 자료형을 복사할 때는 다른 방법을 고려하는 것이 좋음

 

참조로 전달에서 인수와 매개변수는 동일한 객체입니다. 따라서 메모리를 추가로 할당하지 않음

값으로 전달하는 것처럼 복사하는 형식이 아니다

다만 참조로 전달을 사용할 때는 매개변수에 값을 지정할 수 없다는 점을 유의

void fun(int& rX) { … } // 함수 정의: 정수형 변수에 대한 참조를 매개변수로 받음
fun(5);                 // 함수 호출: 오류 발생 (리터럴 5는 lvalue가 아님 → 참조로 바인딩 불가)

 

클래스의 복사 생성자를 만드는 경우는 어떨까?

객체를 전달해야 하므로 값으로 전달은 복사 비용이 많이 들 수 있음

따라서 const 한정자를 붙인 참조를 사용해야 함

 

 

참조로 리턴

 

값으로 리턴은 굉장히 간단하게 매개변수 또는 지역 변수의 값을 리턴할 수 있음

유일한 단점은 복사 비용

리턴해야 하는 객체가 기본 자료형이라면 괜찮지만

클래스 자료형이라면 복사 생성자가 호출될 것이며, 일반적으로 복사에 높은 비용이 들어감

 

참조로 리턴은 복사 비용이 없지만, 일반적인 매개변수와 지역 변수의 값을 리턴할 수 없음

함수가 종료될 때 모든 일반 매개변수와 지역 변수의 값이 파괴되기 때문이다

객체가 파괴되면 원본 변수가 파괴되므로, 참조 변수가 가리키는 메모리 위치 자체가 사라짐

반대로 말하면

참조 매개변수와 정적 지역 변수는 함수가 종료되어도 파괴되지 않으므로 리턴할 수 있음

 

// 참조로 반환하는 함수 정의
int& larger(int& x, int& y)
{
    if (x > y)
    {
        return x; // x가 더 크면 x를 참조로 반환
    }
    return y;     // y가 더 크면 y를 참조로 반환
}

int main()
{
    int x = 10;      // 변수 x 선언 및 초기화
    int y = 20;      // 변수 y 선언 및 초기화
    int z = larger(x, y); // larger 함수 호출 → 더 큰 값의 참조를 받아서 복사해 z에 저장
    cout << z;       // z 출력 → 결과는 20
    return 0;
}

즉 z에 들어간 것은 y의 복사값인 20이 아니라

y 그 자체가 들어간 것이다.

그렇다고 z를 바꾸면 y가 바뀌는가?

그건 아니다

z는 y의 값을 다시 복사해서 받은 것이다

 

int& result = larger(x, y); // result는 y에 대한 참조
result = 100;               // y가 실제로 100으로 바뀜

대신 이렇게 작성하면

y의 값이 100으로 바뀐다

 

 

반응형

댓글