나무모에 미러 (일반/어두운 화면)
최근 수정 시각 : 2024-10-02 13:27:22

참조

참조자에서 넘어옴
1. 한국어의 단어2. 프로그래밍 언어의 기능
2.1. Java2.2. C#2.3. C++
2.3.1. 주의할 점2.3.2. const 참조
2.4. 관련 문서

1. 한국어의 단어

파일:상세 내용 아이콘.svg   자세한 내용은 참고와 참조 문서
번 문단을
부분을
참고하십시오.

2. 프로그래밍 언어의 기능

Reference. 다른 변수를 가리키는 변수로, 포인터와 유사한 기능을 한다.[1]

2.1. Java

Java에서는 형식 일부가 '참조 형식'이라는 특별한 범주로 분류되며 배열, 열거, 클래스, 인터페이스가 여기에 속해 있다. 이들의 변수를 참조 변수(reference variable) 혹은 객체 참조(object reference)라고 한다. 이 변수 각각은 포인터처럼 인스턴스의 주소를 값으로 갖는다. C의 포인터와 다른 점은 정수나 다른 종류의 클래스를 강제로 넣는 등의 강제 형변환이 막혀 있다는 점이다.

클래스의 인스턴스를 만들기 위해서는 해당 클래스의 생성자나 생성 메서드를 이용해 호출해야 한다. 이를 상술한 참조 변수에 할당한다면 해당 인스턴스를 이용할 수 있다. C의 포인터처럼 변수에 인스턴스가 이미 할당되어 있더라도 다른 인스턴스를 해당 변수에 대입할 수도 있다. 참조 변수는 주소를 값으로 갖기 때문에 대입할 때마다 값 복사가 아닌 참조 복사가 발생한다.
The Java programming language does not pass objects by reference; it passes object references by value. Because two copies of the same reference refer to the same actual object, changes made through one reference variable are visible through the other. There is exactly one parameter passing mode pass by value and that helps keep things simple.
-- Arnold, K., Gosling J., Holmes D. (2006). The Java Programming Language Fourth Edition. Boston: Addison-Wesley.
이 참조 변수는 어디까지나 주소를 값으로서 복사하여 대입하는 것이므로, 참조에 의한 호출이 아니라 값에 의한 호출에 해당한다. 더 따지자면 Call by Address에도 해당한다.

#!syntax java
public static void foo(Dog d) {
    d.getAge() == 5; // true
    // change d inside of foo() to point to a new Dog instance 50
    d = new Dog(3);
    d.getAge() == 3; // true
}
public static void main(String[] args) {
    Dog aDog = new Dog(5);
    Dog oldDog = aDog;

    // we pass the object to foo
    foo(aDog);
    // aDog variable is still pointing to the "Max" dog when foo(...) returns
    d.getAge() == 5; // true
    d.getAge() == 3; // false
    aDog == oldDog; // true
}

2.2. C#

C#도 Java처럼 배열, 클래스, 인터페이스는 참조 형식으로 되어 있다. 그런데 C#에서는 C++처럼 구조체를 이용해 임의의 값 타입을 선언할 수 있고, 이를 ref 키워드로 참조자를 선언할 수도 있다. 대체로는 Java처럼 모든 형식을 값에 의한 전달로 사용하지만, 아예 통달한다고 한다면 이 부분에 한하여 아예 모두 값 형식이고 참조자를 통해 참조 전달할 수 있는 C++보다 난해한 점이 있다.

C#에서는 값 형식을 값으로 전달, 참조 형식을 값으로 전달(Java), 값 형식을 참조로 전달(C++), 참조 형식을 참조로 전달하는 것 전부를 지원한다.

2.3. C++

C++의 참조는 다른 언어와는 조금 다르다. 만드는 순간에만 참조할 변수를 선택할 수 있고 한번 만들어진 참조는 변경할 수 없다. 이는 다른 언어는 포인터를 참조가 완전히 대체하지만 C++은 그렇지 않기 때문.
예를 들어, int형 변수 var를 참조하는 참조자를 만들려면
#!syntax cpp
int& ref = var;

라고 쓰면 된다.[2] 이로서 var과 ref는 같은 메모리 공간을 나타내게 된다.

값에 액세스할 때는 * 표시가 필요 없이 그냥 변수처럼 쓸 수 있다.
#!syntax cpp
ref = 1; //var = 1;과 동일


어떤 함수가 참조자를 리턴하고, 반환형이 참조형일 경우 반환값은 참조자 그 자체이다.
#!syntax cpp
int& Example(int& pref) {
  pref++;
  return pref;
}

int main() {
  int num = 20;
  int& ref = Example(num); //int& ref = pref;와 동일
  printf("%d\n", ref);
  return 0;
}

이렇게 하면 pref가 참조하던 변수 num을 ref도 참조하게 된다.

함수의 매개변수에 사용하는 것도 가능하다.
#!syntax cpp
template <typename T>
void swap(T& a, T& b) {
  T tmp = a;
  a = b;
  b = tmp;
}

다만 이 경우 주의할 점이 있다. 아래 '주의할 점' 문단을 참조.

참조자는 반드시 선언과 동시에 변수를 참조하여야 한다. 따라서 아래와 같이 먼저 참조자를 선언해놓고 나중에 변수를 참조하는 건 안 된다.
#!syntax cpp
int& ref; //컴파일 에러


상수를 참조하는 것 역시 안 된다.
#!syntax cpp
int& ref = 20; //컴파일 에러


주소값을 반환받는 것도 불가능하다. 주소값 역시 상수이기 때문이다.
#!syntax cpp
int num = 20;
int& ref = &num; //컴파일 에러


이미 선언된 참조자가 다른 변수를 참조하는 것도 안 된다. 아래와 같은 문장은 num을 참조하는 게 아니라, 원래 참조하던 변수 var의 값을 num의 값으로 바꾸는 것이 된다.
#!syntax cpp
int& ref = var;
ref = num;


NULL로 초기화하는 것도 불가능하다.[3]
#!syntax cpp
int& ref = NULL; //컴파일 에러

2.3.1. 주의할 점

다음의 코드를 보자.
#!syntax cpp
void func(int& r) {
  ...
}

int v = 30;
func(v);
printf("%d\n", v);

C의 포인터를 이용하면 v의 값이 100% 30으로 출력되지만, 위처럼 C++의 참조자를 사용하면 얼마가 나올지 알 수 없다. Call by Reference에 의하여 매개변수로 선언된 참조자 rfunc() 함수 내에서 인자 v의 값을 바꿀 수 있기 때문이다. 이러한 참조자의 사용은 코드의 가독성을 해치게 된다.

2.3.2. const 참조

바로 이러한 문제를 해결하기 위해 const 참조자가 사용된다. const 참조자는 자신이 참조하는 변수의 값을 바꿀 수 없다. 즉 아래의 예시처럼 r을 const 참조자로 만들면, func() 함수 내에서 r의 값(=v의 값)을 수정할 수 없고 접근만 허용되므로 v의 값이 100% 30임을 보장할 수 있다.
#!syntax cpp
void func(const int& r) {
  ...
}

int v = 30;
func(v);
printf("%d\n", v);


const 참조자는 const 변수를 참조할 수 있다.
#!syntax cpp
const int num = 20;
int& ref = num; //컴파일 에러
const int& cRef = num;


또한 const 참조자는 상수도 참조할 수 있다. 단, 엄밀히 말하면 상수 자체가 아니라 컴파일러에 의해 내부적으로 생성된 임시변수에 상수를 복사한 다음, 그 임시변수를 참조하는 것이다.
#!syntax cpp
const int& ref = 5;


const 참조자의 또다른 기능은 임시 값(temporary value)를 참조할 수 있다는 것이다. 아래 코드에서 ref에 실제로 대입되는 것은 "Temp" 자체가 아닌 "Temp"가 위치한 주소 값이다. 즉 원본이 아니라 원본에서 파생된 임시 값이 전달되므로 일반적인 참조자로는 참조가 불가능하다. 이럴 때 필요한 것이 const 참조자이다. 참고로, &가 두 개 붙은 r-value 참조자(ex: std::string&& ref) 역시 임시 값을 참조할 수 있다.
#!syntax cpp
const std::string& ref = "Temp";

2.4. 관련 문서



[1] 참조자도 내부적으로는 포인터로 변환되어 동작하기 때문에, 사실상 동일한 기능이다. 생기게 된 배경으로, 포인터는 매우 강력하지만 잘못 사용할 경우 문제를 발생시키기 때문에 포인터의 장점을 그대로 사용하고 단점을 보완하기 위해서 생긴 기능이다.[2] & 연산자가 이미 선언된 변수의 앞에 붙으면 해당 변수의 주소값을 반환하라는 의미지만, 새로 선언되는 변수의 앞에 붙으면 참조자를 선언하는 의미를 가진다.[3] NULL은 매크로이며 0의 값을 가진다.