참조자도 메모리 공간에 할당되나요?

흔히 C++의 참조자를 설명할 때 참조 대상에 붙이는 새로운 이름이라고 설명하는데 인터넷에 보니까 익명으로 가려진 변수가 할당된다는 말도 있어서 조금 헷갈리네요. 참조자 선언 시 새로운 변수가 메모리 공간에 할당되나요?

1답변

  • 좋아요

    1

    싫어요
    채택취소하기

    http://en.cppreference.com/w/cpp/language/reference 에서 아래와 같은 내용을 볼 수 있습니다.

    References are not objects; they do not necessarily occupy storage, although the compiler may allocate storage if it is necessary to implement the desired semantics (e.g. a non-static data member of reference type usually increases the size of the class by the amount necessary to store a memory address).

    참조자는 객체가 아니기 때문에 저장 저장 공간을 반드시 필요하지 않지만, 컴파일러가 필요하다면 구현에 따라 저장 공간을 차지할 수 있다는 내용입니다. 즉, 메모리 공간에 할당될 수 도 있고 안될 수 도 있습니다.

    위에서도 말했듯이 참조자는 객체가 아니기 때문에 sizeof 를 통해 크기를 측정할 수 없습니다. sizeof(int&)sizeof(int) 의 값과 동일하게 계산됩니다. 따라서 아래와 같은 방법을 통해 참조자의 크기를 측정할 수 있습니다.

    #include <iostream>
    
    struct IntRef {
        int& ref;
    };
    struct CharRef {
        char& ref;
    };
    int main() {
        std::cout << "sizeof int& : " << sizeof(IntRef) << std::endl;
        std::cout << "sizeof char& : " << sizeof(CharRef) << std::endl;
        std::cout << "sizeof void* : " << sizeof(void*) << std::endl;
        return 0;
    }
    

    위 코드를 실행한 결과는 아래와 같습니다. 크기는 시스템 마다 다를 수 있으나 void* 와 참조자의 크기가 같은 것을 볼 수 있습니다.

    sizeof int& : 8
    sizeof char& : 8
    sizeof void* : 8
    

    어셈블리를 확인하여 크기를 알 수 도 있는데요. 아래는 참조자를 사용한 C++ 코드를 GCC 7.3 으로 컴파일 했을 때의 어셈블리 코드 입니다(https://godbolt.org/g/peksus).

    int main() {
        int value = 10;
        int& ref = value;
        int* ptr = &value;
        return 0;
    }
    
    main:
      push rbp
      mov rbp, rsp
      mov DWORD PTR [rbp-20], 10
      lea rax, [rbp-20]
      mov QWORD PTR [rbp-8], rax
      lea rax, [rbp-20]
      mov QWORD PTR [rbp-16], rax
      mov eax, 0
      pop rbp
      ret
    

    여기서 mov QWORD PTR [rbp-8], raxint& ref = value;, mov QWORD PTR [rbp-16], raxint* ptr = &value; 의 내용입니다.

    rbp 다음의 숫자만 다른것을 볼 수 있습니다. 즉, 참조자는 포인터와 실제 동작이 동일하며 rbp-20 가 아닌 rbp-8 이란 위치에 자신만의 메모리 공간을 차지합니다.

    정리하면 표준상 참조자는 메모리 공간을 반드시 필요하지 않으나 컴파일러의 구현에 따라 메모리 공간을 차지하며, 그 크기 역시 구현에 따라 다르지만 일반적으로 포인터와 같은 크기의 메모리 공간을 차지합니다.

    • 답변 감사합니다! 인터넷을 보니까 참조자 선언 시 컴파일러가 내부에서 const 포인터를 새로 할당해서 그걸로 계산을 한다는 말도 있더라구요. 근데 외부에서 접근은 불가능하니 참조자 자체가 메모리에 할당되는 건 아니라고 봐야겠네요. 어쨌든 자세한 답변 감사드립니다. JKrig 2018.2.9 13:27
    • const 포인터를 생성하는 것처럼 동작합니다. 하지만 C++ 에서 그 포인터 변수에 직접 접근하는 것은 불가능합니다. 공기는 보이지 않고 만져지지 않지만 존재하는 것 처럼, 참조자도 실제론 메모리가 할당되어 존재합니다. 유동욱 2018.2.9 13:40
    • 안녕하세요. 이제막 C++ 공부하고있는 사람인대 우연히 저도 같은 궁금증에 생겨서 들어오게되었습니다. 1. 답변자님께서 "컴파일러의 구현에 따라" 라고 하셨는대 그럼 같은코드라도 컴파일러에 따라서 메모리공간을 차지할수도있고 차지하지 않을수있다고 이해하면 될까요? 2. 그런데 어떻게 메모리공간을 차지하지않고 참조가 가능한가요? 3. " 포인터 변수는 데이타가 저장된 메모리상 공간의 시작 주소를 저장시키는 변수입니다. 참조자는 데이타가 저장된 메모리상 공산의 시작 주소를 공유하는 이름만 가진 변수 입니다. 포인터 변수는 4바이트의(OS에 따라 다를 수 있음) 메모리 공간을 따로 할당 받으며 그 공간에 위에 설명한 주소값을 저장시키게 됩니다. 즉, 변수로서 주소값이라는 데이타를 저장하는 메모리 공간을 가지고 있습니다. 주의할점은 데이타를 저장하는 공간이 아니라 데아타의 주소를 저장하는 공간이라는 것입니다. 참조자는 변수의 형태를 취하기는 하지만 변수는 아닙니다. 따로 메모리 공간을 할당 받지 않으면 위에서 설명한것처럼 데이타의 주소를 이름만 가지고 공유하는 형태 입니다. " 네이버 답변을 이렇게 하신분이 있는데 이것은 잘못된 답변이죠? 유원준 2018.10.15 13:13
    • 댓글로는 줄내림이 되지 않아서 말씀드리기가 조콤 어렵지만 노력해 보겠습니다. 1) 네, 같은 코드를 작성하여도 다를 수 있습니다. 이는 컴파일러 종류 뿐 아니라 최적화 옵션에도 영향을 받을 수 있습니다. 다만 본 답변에서도 말씀드린것 처럼 class 또는 struct의 멤버로 참조자가 사용될 경우 또는 파라미터로 사용될 경우 거의 대부분 메모리 공간이 할당되게 됩니다. 2) 결국 참조자는 다른 변수 또는 메모리의 별칭입니다. a란 변수가 있고 이를 참조자 b로 참조하고 b를 사용할 경우 컴파일러는 구지 b를 위해 메모리르 할당할 필요가 없습니다. 코드에 b라고 써있어도 이걸 단순히 a로 치환해서 컴파일하며 되기 때문입니다. 3) 네이버 답변은 맞는 답변입니다. 변수는 가 메모리 공간을 갖는 다는 것은 변수가 가질 값을 담을 곳을 갖는 다는 것을 의미합니다. int 변수는 정수를 저장기 위해서 메모리 공간을 확보하지만 int 참조자는 정수를 저장하기 위해서 메모리 공간을 확보하지 않습니다. 다만 다른 변수나 메모리를 참조하기 위해서 주소를 저장할 공간을 확보할 수 있습니다. 이는 앞서 말한 것처럼 구현에 따라 상황에 따라 확보할 수 도있고 단순히 변수 이름만 치환되는 식으로 처리될 수도 있기에 주소를 저장할 공간을 확보할 수도 없을수도 있습니다. 참조자는 결국 포인터의 다른 버전이기에 네이버 답변 처럼 말해도 틀린 것은 아닙니다. 그리고 제가 답변한 내용은 표준 상 그렇다는거지 대부분의 컴파일러가 포인터 처럼 주소를 저장할 공간을 생성하고 있습니다. 그리고 포인터 또한 상황에 따라 주소공간을 할당 받지 않도록 처리될 수 있기는 합니다. 유동욱 2018.10.15 20:44

ᕕ( ᐛ )ᕗ
로그인이 필요합니다

작성한 답변에 다른 개발자들이 댓글을 작성하거나 댓글에 좋아요/싫어요를 할 수 있기 때문에 계정을 필요로 합니다.