c++ - 타입 - 포인터 의 참조 자




C++에서 포인터 변수와 참조 변수의 차이점은 무엇입니까? (20)

나는 참조가 문법적인 설탕이라는 것을 안다. 그래서 코드는 읽고 쓰는 것이 더 쉽다.

그러나 차이점은 무엇입니까?

아래 답변 및 링크 요약 :

  1. 바인딩 후 참조를 다시 할당 할 수 없으면 포인터를 여러 번 다시 할당 할 수 있습니다.
  2. 포인터는 아무데도 가리킬 수 없으며 ( NULL ), 참조는 항상 객체를 나타냅니다.
  3. 포인터로 할 수있는 것처럼 참조 주소를 가져올 수 없습니다.
  4. "참조 연산"은 없습니다 (그러나 참조로 가리키는 객체의 주소를 가져 와서 &obj + 5 에서처럼 포인터 연산을 할 수 있습니다).

오해를 명확히하기 위해 :

C ++ 표준은 컴파일러가 참조를 구현하는 방법을 피하기 위해 매우 신중하지만 모든 C ++ 컴파일러는 참조를 포인터로 구현합니다. 즉, 다음과 같은 선언입니다.

int &ri = i;

완전히 최적화되지 않은 경우 포인터와 동일한 양의 저장소를 할당하고 해당 주소를 해당 저장소에 저장합니다.

따라서 포인터와 참조는 모두 동일한 양의 메모리를 사용합니다.

일반적으로,

  • 유용하고 자체적으로 문서화 할 수있는 인터페이스를 제공하기 위해 함수 매개 변수와 반환 유형에서 참조를 사용하십시오.
  • 알고리즘과 데이터 구조를 구현하기위한 포인터를 사용하십시오.

재미있는 읽기 :


C ++ 참고 자료 ( C 프로그래머 용 )

참조상수 포인터 (상수 값에 대한 포인터와 혼동해서는 안됩니다!)와 자동 간접 지정으로 생각할 수 있습니다. 즉, 컴파일러는 * 연산자를 적용합니다.

모든 참조는 널이 아닌 값으로 초기화되어야하며 그렇지 않으면 컴파일이 실패합니다. 참조 주소를 얻는 것도 불가능합니다. 대신 주소 연산자가 참조 된 값의 주소를 반환합니다. 참조에 대해 연산을 수행 할 수도 없습니다.

C 프로그래머는 간접적 인 일이 발생하거나 인수가 함수 시그니처를 보지 않고 값이나 포인터로 전달되는 경우 더 이상 명확하지 않으므로 C ++ 참조를 싫어할 수 있습니다.

C ++ 프로그래머는 포인터가 안전하지 않은 것으로 간주하기 때문에 포인터를 사용하는 것을 싫어할 수 있습니다. 비록 사소한 경우를 제외하고는 참조가 항상 포인터보다 안전하지는 않지만 자동 간접 지정의 편의성이없고 다른 의미 론적 의미를 지닙니다.

C ++ FAQ 에서 다음 문장을 고려하십시오.

참조가 기본 어셈블리 언어의 주소를 사용하여 구현 되더라도 참조가 객체에 대한 재미있는 포인터라고 생각하지 마십시오 . 참조 객체입니다. 객체에 대한 포인터가 아니며 객체의 복사본도 아닙니다. 그것은 대상입니다.

그러나 참조가 실제로 대상 일 경우 참조가 매달릴 수 있습니까? 관리되지 않는 언어에서는 참조가 포인터보다 '더 안전'하지 못합니다. 일반적으로 범위 경계에서 값을 신뢰할 수있게 별칭을 지정하는 방법이 아닙니다.

왜 내가 C ++ 참조가 유용하다고 생각 하는가?

C 배경에서 나온 C ++ 참조는 다소 어리석은 개념처럼 보이지만 가능하면 포인터 대신 자동 참조가 사용되어야합니다. 자동 간접 참조가 편리하며 참조가 RAII 다룰 때 특히 유용합니다. 장점 이라기보다는 관용구 코드를 덜 어색하게 만든다.

RAII는 C ++의 핵심 개념 중 하나이지만, 의미를 복사하는 것과는 거의 상호 작용하지 않습니다. 참조로 개체를 전달하면 복사가 필요 없으므로 이러한 문제를 피할 수 있습니다. 언어에서 참조가 없으면 대신 포인터를 사용해야하므로 사용하기가 번거롭므로 대안보다 모범 사례 솔루션이 쉬워야한다는 언어 디자인 원칙을 위반합니다.


포인터와 참조의 차이점

포인터는 0으로 초기화되고 참조는 초기화되지 않습니다. 실제로 참조는 객체를 참조해야하지만 포인터는 null 포인터가 될 수 있습니다.

int* p = 0;

그러나 우리는 가질 수없고 int& p = 0;또한 가질 수 없습니다.int& p=5 ; .

실제로 제대로 수행하려면 먼저 객체를 선언하고 정의 했어야합니다. 그런 다음 객체에 대한 참조를 만들 수 있습니다. 따라서 이전 코드의 올바른 구현은 다음과 같습니다.

Int x = 0;
Int y = 5;
Int& p = x;
Int& p1 = y;

또 다른 중요한 점은 초기화없이 포인터를 선언 할 수 있다는 것입니다. 그러나 참조가 항상 변수 또는 객체를 참조해야하는 경우에는 그렇게 할 수 없습니다. 그러나 포인터의 사용은 위험하므로 일반적으로 포인터가 실제로 무언가를 가리키고 있는지 확인합니다. 참조의 경우 해당 검사가 필요하지 않습니다. 왜냐하면 선언 중에 객체에 대한 참조가 필수적이라는 것을 이미 알고 있기 때문입니다.

또 다른 차이점은 포인터가 다른 객체를 가리킬 수 있지만 참조는 항상 같은 객체를 참조한다는 것입니다. 다음 예제를 보겠습니다.

Int a = 6, b = 5;
Int& rf = a;

Cout << rf << endl; // The result we will get is 6, because rf is referencing to the value of a.

rf = b;
cout << a << endl; // The result will be 5 because the value of b now will be stored into the address of a so the former value of a will be erased

또 다른 요점 : STL 템플릿과 같은 템플릿이있을 때 이러한 종류의 클래스 템플릿은 포인터가 아닌 참조를 반환하므로 operator []를 사용하여 쉽게 새 값을 읽거나 할당 할 수 있습니다.

Std ::vector<int>v(10); // Initialize a vector with 10 elements
V[5] = 5; // Writing the value 5 into the 6 element of our vector, so if the returned type of operator [] was a pointer and not a reference we should write this *v[5]=5, by making a reference we overwrite the element by using the assignment "="

가장 중요한 부분을 잊어 버렸습니다.

포인터를 사용한 구성원 액세스 사용 ->
참조가있는 회원 액세스 .

foo.barviEmacs 보다 분명히 우수하다는 것과 같은 방식으로 foo->bar 보다 분명히 우월합니다 :-)


구문 당과는 별도로 참조는 const 포인터입니다 ( const 포인터가 아닙니다 ). 참조 변수를 선언 할 때 참조하는 내용을 설정해야하며 나중에 변경할 수 없습니다.

업데이트 : 이제 생각해 보면 더 중요한 차이가 있습니다.

const 포인터의 대상은 주소를 가져 와서 const 캐스트를 사용하여 바꿀 수 있습니다.

참조의 대상은 UB가 부족한 어떤 방법 으로든 대체 할 수 없습니다.

이렇게하면 컴파일러가 참조에서 더 많은 최적화 작업을 수행 할 수 있습니다.


사실, 참조는 실제로 포인터와 같지 않습니다.

컴파일러는 이름을 메모리 주소와 연결하여 변수에 대한 "참조"를 유지합니다. 컴파일 할 때 변수 이름을 메모리 주소로 변환하는 것이 그 일입니다.

참조를 만들 때 포인터 변수에 다른 이름을 할당한다는 것을 컴파일러에만 알립니다. 그래서 참조가 "null을 가리킬"수없는 이유는 변수가 될 수 없기 때문입니다.

포인터는 변수입니다. 그들은 다른 변수의 주소를 포함하거나 null 일 수 있습니다. 중요한 것은 참조가 참조하는 변수 만 갖는 반면 포인터에는 값이 있다는 것입니다.

이제 실제 코드에 대한 몇 가지 설명이 있습니다.

int a = 0;
int& b = a;

여기 a 를 가리키는 또 다른 변수를 생성하지 않습니다. 당신은 단지 a 의 값을 a 메모리 내용에 다른 이름을 추가 a 것이다. 이 메모리에는 이제 두 개의 이름 인 ab 가 있으며 두 개의 이름을 사용하여 주소 지정할 수 있습니다.

void increment(int& n)
{
    n = n + 1;
}

int a;
increment(a);

함수를 호출 할 때 컴파일러는 일반적으로 인수를 복사 할 메모리 공간을 생성합니다. 함수 시그니처는 생성되어야하는 공간을 정의하고이 공간에 사용해야하는 이름을 제공합니다. 매개 변수를 참조로 선언하면 메서드 호출 중에 새 메모리 공간을 할당하는 대신 입력 변수 메모리 공간을 사용하도록 컴파일러에 지시합니다. 함수가 호출 범위에서 선언 된 변수를 직접 조작하지만 컴파일 된 코드를 실행할 때 더 이상 범위가 없다는 것을 기억하는 것이 이상하게 보일 수 있습니다. 평범한 평면 메모리가 있으며 함수 코드는 모든 변수를 조작 할 수 있습니다.

이제 컴파일러가 extern 변수를 사용할 때와 같이 컴파일 할 때 참조를 알 수없는 경우가있을 수 있습니다. 따라서 참조는 기본 코드에서 포인터로 구현되거나 구현되지 않을 수 있습니다. 그러나 내가 여러분에게 준 예제에서는 포인터로 구현되지 않을 가능성이 큽니다.


정말로 pedagonic하고 싶다면 포인터로 할 수없는 참조를 사용하여 할 수있는 한 가지가 있습니다. 임시 객체의 수명을 연장하는 것입니다. C ++에서 const 참조를 임시 객체에 바인딩하면 해당 객체의 수명이 참조의 수명이됩니다.

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

이 예제에서 s3_copy는 연결의 결과 인 임시 객체를 복사합니다. 본질적으로 s3_reference는 임시 객체가됩니다. 실제로는 참조와 동일한 수명을 갖는 임시 객체에 대한 참조입니다.

const 없이 이것을 시도하면 컴파일에 실패합니다. 임시 오브젝트에 const가 아닌 참조를 바인드 할 수 없으며, 그 문제에 대해 주소를 지정할 수도 없습니다.


참조는 포인터와 매우 유사하지만 컴파일러 최적화에 도움이되도록 특별히 제작되었습니다.

  • 참조는 컴파일러가 어떤 참조 별칭을 추적하는지 쉽게 추적 할 수 있도록 설계되었습니다. "참조 연산"이없고 참조를 재 할당하지 않는 두 가지 주요 기능이 매우 중요합니다. 이것들은 컴파일러가 어떤 참조를 컴파일 할 때 어떤 변수에 알릴지를 알 수있게합니다.
  • 참조는 컴파일러가 레지스터에 넣는 것과 같이 메모리 주소가없는 변수를 참조 할 수 있습니다. 지역 변수의 주소를 취하면 컴파일러가 레지스터에 넣기가 매우 어렵습니다.

예로서:

void maybeModify(int& x); // may modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // This function is designed to do something particularly troublesome
    // for optimizers. It will constantly call maybeModify on array[0] while
    // adding array[1] to array[2]..array[size-1]. There's no real reason to
    // do this, other than to demonstrate the power of references.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(array[0]);
        array[i] += array[1];
    }
}

최적화 컴파일러는 우리가 [0]과 [1] 꽤 많은 묶음에 접근하고 있다는 것을 깨닫게됩니다. 알고리즘을 최적화하여 다음을 수행하는 것이 좋습니다.

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Do the same thing as above, but instead of accessing array[1]
    // all the time, access it once and store the result in a register,
    // which is much faster to do arithmetic with.
    register int a0 = a[0];
    register int a1 = a[1]; // access a[1] once
    for (int i = 2; i < (int)size; i++) {
        maybeModify(a0); // Give maybeModify a reference to a register
        array[i] += a1;  // Use the saved register value over and over
    }
    a[0] = a0; // Store the modified a[0] back into the array
}

이러한 최적화를 수행하려면 호출 중에 배열 [1]을 변경할 수있는 것이 없음을 증명해야합니다. 이것은하기 쉽습니다. 나는 결코 2보다 작지 않으므로 array [i]는 배열 [1]을 결코 참조 할 수 없다. maybeModify ()는 a0을 참조로 제공합니다 (별칭 배열 [0]). "참조"연산이 없기 때문에 컴파일러는 maybeModify가 x의 주소를 얻지 못한다는 것을 증명해야하며 배열 [1]을 변경하지 않는다는 것이 증명되었습니다.

또한 a0에 임시 레지스터 복사본이있는 동안 향후 호출이 [0]을 읽고 쓸 수있는 방법이 없음을 증명해야합니다. 많은 경우에 참조가 클래스 인스턴스와 같은 영구 구조에 저장되지 않는다는 것이 명백하기 때문에 이것은 흔히 증명하기가 쉽지 않습니다.

이제 포인터로 같은 작업을 수행하십시오.

void maybeModify(int* x); // May modify x in some way

void hurtTheCompilersOptimizer(short size, int array[])
{
    // Same operation, only now with pointers, making the
    // optimization trickier.
    for (int i = 2; i < (int)size; i++) {
        maybeModify(&(array[0]));
        array[i] += array[1];
    }
}

동작은 동일합니다. maybeModify가 배열 [1]을 수정하지 않는다는 것을 증명하는 것이 훨씬 어렵습니다. 왜냐하면 우리가 이미 포인터를 주었기 때문입니다. 고양이는 가방에서 나와요. 이제는 훨씬 더 어려운 증명을해야합니다 : xModule에 결코 쓰지 않는 것을 증명하는 maybeModify의 정적 분석 또한 배열 [0]을 참조 할 수있는 포인터를 절대로 저장하지 않아야한다는 것을 증명해야합니다. 까다로워.

현대 컴파일러는 정적 분석에서 더 좋아지고 있습니다.하지만 참조를 사용하고 도움을주는 것이 좋습니다.

물론, 영리한 최적화가 없다면, 컴파일러는 필요할 때 참조를 실제로 포인터로 바꿀 것입니다.

편집 : 5 년이 답변을 게시 한 후 참조가 동일한 주소 지정 개념을 보는 다른 방법보다 다른 실제 기술적 차이를 발견했습니다. 참조는 포인터가 할 수없는 방식으로 임시 객체의 수명을 수정할 수 있습니다.

F createF(int argument);

void extending()
{
    const F& ref = createF(5);
    std::cout << ref.getArgument() << std::endl;
};

일반적으로 createF(5) 호출하여 생성 된 객체와 같은 임시 객체는 표현식의 끝에서 삭제됩니다. 그러나, 그 객체를 참조로 바인딩함으로써, C ++은 ref 가 범위를 벗어날 때까지 그 임시 객체의 수명을 연장 할 것입니다.


그것이 차지할 공간의 어떤 부작용 (코드 실행없이)을 실제로 볼 수 없기 때문에 어느 정도의 공간이 차지하는지는 중요하지 않습니다.

반면에 참조와 포인터 사이의 주요한 차이점은 const 참조가 범위를 벗어날 때까지 const 참조에 할당 된 임시 변수가 살아 있다는 것입니다.

예 :

class scope_test
{
public:
    ~scope_test() { printf("scope_test done!\n"); }
};

...

{
    const scope_test &test= scope_test();
    printf("in scope\n");
}

인쇄 할 것입니다 :

in scope
scope_test done!

ScopeGuard가 작동 할 수있게 해주는 언어 메커니즘입니다.


나는 여기에서 다루지 않은 또 다른 요점이있는 것처럼 느낀다.

포인터와 달리 참조는 참조 하는 객체 와 구문 론적으로 동일 합니다. 즉, 객체에 적용 할 수있는 모든 작업은 참조와 동일한 구문 (예외는 물론 초기화)에서 작동합니다.

이것이 피상적으로 보일 수도 있지만,이 속성은 다음과 같이 많은 C ++ 기능에 중요하다고 생각합니다.

  • 템플릿 . 템플릿 매개 변수는 덕 형식이므로 형식의 구문 속성이 중요하므로 동일한 템플릿을 모두 T및에서 사용할 수 있습니다 T&.
    (또는 std::reference_wrapper<T>정지에 대한 암시 적 캐스트에 의존하는 T&)
    모두를 포함 템플릿 T&T&&훨씬 더 일반적이다.

  • Lvalues . str[0] = 'X';C-strings ( char* str) 에서만 작동하는 참조가없는 문을 고려하십시오 . 문자를 참조로 반환하면 사용자 정의 클래스가 동일한 표기법을 사용할 수 있습니다.

  • 생성자를 복사하십시오 . 구문 적으로 객체에 포인터가 아닌 객체를 복사 생성자에 전달하는 것이 좋습니다. 그러나 복사 생성자가 값으로 객체를 가져올 수있는 방법은 없습니다. 동일한 복사 생성자에 대한 재귀 호출이 발생합니다. 여기서는 유일한 참조로 참조를 남깁니다.

  • 운영자 과부하 . 참고 문헌을 operator+(const T& a, const T& b)통해 동일한 중침 표기법을 유지하면서 연산자 호출에 간접 참조를 도입 할 수 있습니다 . 이는 또한 일반적인 오버로드 된 함수에서도 작동합니다.

이러한 점들은 C ++과 표준 라이브러리의 상당 부분을 지원하므로 참조 용으로 사용됩니다.


또 다른 차이점은 void 타입에 대한 포인터를 가질 수 있다는 것입니다 (그리고 포인터는 무엇이든 의미합니다). 그러나 void에 대한 참조는 금지되어 있습니다.

int a;
void * p = &a; // ok
void & p = a;  //  forbidden

나는이 특별한 차이에 대해 정말로 행복하다고 말할 수 없다. 나는 주소를 가진 어떤 것에도 의미의 참조를 허용하고 그렇지 않으면 참조를 위해 같은 행동을하는 것이 훨씬 더 좋을 것이다. 그것은 참조를 사용하여 memcpy와 같은 C 라이브러리 함수의 일부 동등한 것을 정의 할 수 있습니다.


참조의 또 다른 흥미로운 사용법은 사용자 정의 형식의 기본 인수를 제공하는 것입니다.

class UDT
{
public:
   UDT() : val_d(33) {};
   UDT(int val) : val_d(val) {};
   virtual ~UDT() {};
private:
   int val_d;
};

class UDT_Derived : public UDT
{
public:
   UDT_Derived() : UDT() {};
   virtual ~UDT_Derived() {};
};

class Behavior
{
public:
   Behavior(
      const UDT &udt = UDT()
   )  {};
};

int main()
{
   Behavior b; // take default

   UDT u(88);
   Behavior c(u);

   UDT_Derived ud;
   Behavior d(ud);

   return 1;
}

디폴트 플레이버는 '참조에 대한 임시 속성에 대한 바인딩 참조'를 사용합니다.


컴퓨터 언어를 추상적 또는 학술적 방식으로 공부하는 것에 익숙하지 않은 경우 밀교 적으로 나타날 수있는 의미상의 차이가 있습니다.

최상위 레벨에서 참조 개념은 투명한 "별칭"입니다. 컴퓨터가 주소를 사용하여 작동하도록 할 수도 있지만 걱정하지 않아도됩니다. 기존 개체의 "다른 이름"으로 간주해야하며 구문에 반영됩니다. 포인터보다 더 엄격하므로 매달린 참조를 만들 때보다 매달린 포인터를 만들 때보 다 컴파일러에서보다 확실하게 경고 할 수 있습니다.

그 외에도 물론 포인터와 참조 사이에는 실제적인 차이가 있습니다. 그 (것)들을 사용하기 위하여 통어론은 명백하게 다르, 당신은 참조를 "re-seat", 아무 것도에 참조가 있고, 참고에 포인터가있을 수 없다.


혼란에 빠질 위험이 있으므로, 나는 약간의 입력을 던지고 싶다. 컴파일러가 어떻게 레퍼런스를 구현하는지에 달려 있다고 확신하지만, gcc의 경우 레퍼런스는 스택의 변수만을 가리킬 수 있다는 생각이다. 실제로는 올바르지 않습니다. 예를 들면 다음과 같습니다.

#include <iostream>
int main(int argc, char** argv) {
    // Create a string on the heap
    std::string *str_ptr = new std::string("THIS IS A STRING");
    // Dereference the string on the heap, and assign it to the reference
    std::string &str_ref = *str_ptr;
    // Not even a compiler warning! At least with gcc
    // Now lets try to print it's value!
    std::cout << str_ref << std::endl;
    // It works! Now lets print and compare actual memory addresses
    std::cout << str_ptr << " : " << &str_ref << std::endl;
    // Exactly the same, now remember to free the memory on the heap
    delete str_ptr;
}

어떤 출력이 :

THIS IS A STRING
0xbb2070 : 0xbb2070

메모리 주소조차 똑같은 것을 알게된다면, 참조가 성공적으로 힙의 변수를 가리키고 있음을 의미합니다! 이제 당신이 정말 이상한 사람이되기를 원한다면, 이것도 가능합니다 :

int main(int argc, char** argv) {
    // In the actual new declaration let immediately de-reference and assign it to the reference
    std::string &str_ref = *(new std::string("THIS IS A STRING"));
    // Once again, it works! (at least in gcc)
    std::cout << str_ref;
    // Once again it prints fine, however we have no pointer to the heap allocation, right? So how do we free the space we just ignorantly created?
    delete &str_ref;
    /*And, it works, because we are taking the memory address that the reference is
    storing, and deleting it, which is all a pointer is doing, just we have to specify
    the address with '&' whereas a pointer does that implicitly, this is sort of like
    calling delete &(*str_ptr); (which also compiles and runs fine).*/
}

어떤 출력이 :

THIS IS A STRING

따라서 참조는 후드 아래의 포인터이며, 둘 다 메모리 주소를 저장하고 있습니다. 주소가 가리키는 곳과 관련이 없습니다. std :: cout << str_ref;를 호출하면 어떻게 될 것이라고 생각합니까? 삭제 & str_ref 호출 후? 음, 분명히 잘 컴파일되지만, 더 이상 유효한 변수를 가리키고 있지 않기 때문에 런타임에 세분화 오류가 발생합니다. 범위를 벗어날 때까지는 여전히 존재하는 손상된 참조가 있지만 쓸모가 없습니다.

즉, 참조는 포인터 메 커닉을 추상화 한 포인터 일 뿐이며 더 안전하고 사용하기 쉽습니다 (우발적 인 포인터 수학, 혼합되지 않음, '->'등). 위의 예와 같이 헛소리하지 마라.)

이제 컴파일러가 참조를 처리하는 방법에 관계없이 참조 특정 메모리 주소의 특정 변수를 참조하여 예상대로 작동 해야 하기 때문에 항상 후드 아래에 포인터 가 있습니다 (따라서 용어 '참조').

참조와 함께 기억해야 할 중요한 유일한 규칙은 선언시에 정의되어야한다는 것입니다 (헤더의 참조는 예외입니다.이 경우 헤더가 포함 된 객체가 생성자에 정의되어 있어야합니다. 그것을 정의하기에는 너무 늦었습니다).

위의 예는 참고서가 무엇인지 보여주는 예제 일 뿐이며, 그런 방식으로 참조를 사용하고 싶지는 않습니다. 참고 문헌의 적절한 사용법에 관해서는 머리에 못을 박은 이미 여기에 대한 답변이 많이 있습니다.


C ++에서 포인터에 대한 참조가 가능하지만 그 반대가 불가능하다는 것은 참조에 대한 포인터가 불가능하다는 것을 의미합니다. 포인터에 대한 참조는 포인터를 수정하는 더 명확한 구문을 제공합니다. 이 예제를 보자.

#include<iostream>
using namespace std;

void swap(char * &str1, char * &str2)
{
  char *temp = str1;
  str1 = str2;
  str2 = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap(str1, str2);
  cout<<"str1 is "<<str1<<endl;
  cout<<"str2 is "<<str2<<endl;
  return 0;
}

그리고 위의 프로그램의 C 버전을 고려하십시오. C에서는 포인터에 대한 포인터 (다중 간접 참조)를 사용해야하며 혼동을 일으키고 프로그램이 복잡해 보일 수 있습니다.

#include<stdio.h>
/* Swaps strings by swapping pointers */
void swap1(char **str1_ptr, char **str2_ptr)
{
  char *temp = *str1_ptr;
  *str1_ptr = *str2_ptr;
  *str2_ptr = temp;
}

int main()
{
  char *str1 = "Hi";
  char *str2 = "Hello";
  swap1(&str1, &str2);
  printf("str1 is %s, str2 is %s", str1, str2);
  return 0;
}

포인터에 대한 자세한 내용은 다음을 참조하십시오.

앞에서 말한 것처럼 참조 포인터는 불가능합니다. 다음 프로그램을 사용해보십시오.

#include <iostream>
using namespace std;

int main()
{
   int x = 10;
   int *ptr = &x;
   int &*ptr1 = ptr;
}

두 참조와 포인터는 다른 값에 간접적으로 액세스하는 데 사용되지만 참조와 포인터 사이에는 두 가지 중요한 차이점이 있습니다. 첫 번째는 참조가 항상 객체를 참조한다는 것입니다. 초기화하지 않고 참조를 정의하는 것은 오류입니다. 할당의 동작은 두 번째 중요한 차이입니다. 참조에 지정하면 참조가 바인딩되는 객체가 변경됩니다. 다른 오브젝트에 대한 참조를 리 바인드하지 않습니다. 초기화되면 참조는 항상 동일한 기본 객체를 참조합니다.

이 두 프로그램 단편을 고려하십시오. 먼저 포인터 하나를 다른 포인터에 할당합니다.

int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2;    // pi now points to ival2

할당이 끝나면 pi에 의해 처리 된 객체는 변경되지 않습니다. 할당은 pi 값을 변경하여 다른 객체를 가리 킵니다. 이제 두 개의 참조를 지정하는 유사한 프로그램을 고려하십시오.

int &ri = ival, &ri2 = ival2;
ri = ri2;    // assigns ival2 to ival

이 할당은 ri에 의해 참조 된 값인 ival을 변경하고 참조 자체는 변경하지 않습니다. 할당 후에는 두 개의 참조가 여전히 원래 객체를 참조하므로 해당 객체의 값도 이제는 동일합니다.


두 참조와 포인터는 다른 함수의 한 함수의 지역 변수를 변경하는 데 사용할 수 있습니다. 둘 다 함수에 인수로 전달되거나 함수에서 리턴 될 때 큰 오브젝트의 복사를 저장하여 효율성 향상을 얻을 수 있습니다. 위의 유사점에도 불구하고 참조와 포인터 사이에는 다음과 같은 차이점이 있습니다.

참조는 포인터보다 덜 강력합니다.

1) 참조가 생성되면 나중에 다른 객체를 참조 할 수 없습니다. 다시 장착 할 수 없습니다. 이것은 종종 포인터로 수행됩니다.

2) 참조는 NULL 일 수 없습니다. 포인터는 종종 유효한 것으로 가리키고 있지 않음을 나타 내기 위해 NULL이됩니다.

3) 참조가 선언되면 초기화되어야합니다. 포인터에는 이러한 제한이 없습니다.

위의 제한 사항으로 인해 C ++의 참조는 연결된 목록, 트리 등과 같은 데이터 구조를 구현하는 데 사용할 수 없습니다. Java에서 참조에는 위의 제한이 없으며 모든 데이터 구조를 구현하는 데 사용할 수 있습니다. Java에서 참조가 더 강력 해지면 Java가 포인터를 필요로하지 않는 주된 이유입니다.

참조가 더 안전하고 사용하기 쉽습니다.

1) 안전 : 참조를 초기화해야하므로 야생 포인터와 같은 야생 참조가 존재하지 않을 수 있습니다. 유효한 위치를 참조하지 않는 참조를 가질 수도 있습니다.

2) 사용하기 쉬움 : 참조에 값에 액세스 할 때 역 참조 연산자가 필요하지 않습니다. 일반 변수처럼 사용할 수 있습니다. '&'연산자는 선언시에만 필요합니다. 또한 멤버에 액세스하는 데 화살표 연산자 (->)가 필요한 포인터와 달리 객체 참조의 멤버는 도트 연산자 ( '.')로 액세스 할 수 있습니다.

위의 이유와 함께, 포인터를 사용할 수없는 복사 생성자 인수 같은 곳은 거의 없습니다. 복사 생성자에서 인수를 사용하려면 참조를 사용해야합니다. ++ 같은 연산자를 오버로딩 할 때도 비슷하게 참조를 사용해야합니다 .


이 프로그램은 질문의 답을 이해하는 데 도움이 될 수 있습니다. 이것은 참조 "j"와 변수 "x"를 가리키는 포인터 "ptr"의 간단한 프로그램입니다.

#include<iostream>

using namespace std;

int main()
{
int *ptr=0, x=9; // pointer and variable declaration
ptr=&x; // pointer to variable "x"
int & j=x; // reference declaration; reference to variable "x"

cout << "x=" << x << endl;

cout << "&x=" << &x << endl;

cout << "j=" << j << endl;

cout << "&j=" << &j << endl;

cout << "*ptr=" << *ptr << endl;

cout << "ptr=" << ptr << endl;

cout << "&ptr=" << &ptr << endl;
    getch();
}

프로그램을 실행하고 결과를 살펴보면 이해할 수 있습니다.

또한 10 분을 남겨두고 다음 비디오를 시청하십시오. https://www.youtube.com/watch?v=rlJrrGV0iOg


이는 tutorial 기반으로합니다 . 쓰여진 것은 그것을 더 분명하게합니다 :

>>> The address that locates a variable within memory is
    what we call a reference to that variable. (5th paragraph at page 63)

>>> The variable that stores the reference to another
    variable is what we call a pointer. (3rd paragraph at page 64)

기억하기 만하면,

>>> reference stands for memory location
>>> pointer is a reference container (Maybe because we will use it for
several times, it is better to remember that reference.)

더구나 거의 모든 포인터 자습서를 참조 할 수 있으므로 포인터는 포인터를 배열과 비슷하게 만드는 포인터 산술에 의해 지원되는 객체입니다.

다음 진술을보고,

int Tom(0);
int & alias_Tom = Tom;

alias_Tomint로서 이해 될 수있다 alias of a variable(서로 다른 typedefalias of a type) Tom. 그것은 또한 그러한 진술의 용어를 잊어도 괜찮습니다의 참조를 만드는 것입니다 Tom.


참조는 다른 변수의 별명이지만 포인터는 변수의 메모리 주소를 보유합니다. 참조는 일반적으로 함수 매개 변수로 사용되므로 전달 된 객체는 사본이 아니라 객체 자체입니다.

    void fun(int &a, int &b); // A common usage of references.
    int a = 0;
    int &b = a; // b is an alias for a. Not so common to use. 

참조는 일부 메모리에 주어진 다른 이름이 아닙니다. 이것은 사용법에 따라 자동으로 참조 해제되는 불변 포인터입니다. 근본적으로 그것은 아래로 귀결된다 :

int& j = i;

내부적으로

int* const j = &i;




c++-faq