포인터 대 참조


256

함수에 원래 변수를 제공 할 때 더 나은 방법은 무엇입니까?

unsigned long x = 4;

void func1(unsigned long& val) {
     val = 5;            
}
func1(x);

또는:

void func2(unsigned long* val) {
     *val = 5;
}
func2(&x);

IOW : 다른 것을 골라야 할 이유가 있습니까?


1
참조는 물론 가치가 있지만 포인터가 어디에나있는 C에서 나옵니다. 참조의 가치를 이해하기 위해서는 먼저 포인터에 능숙해야합니다.
Jay D

함수형 프로그래밍의 참조 투명성과 같은 목표에 어떻게 맞습니까? 함수가 항상 새 객체를 반환하고 내부적으로 상태를 변경하지 않도록하려면 (특히 함수에 전달 된 변수가 아닌 경우) 어떻게해야합니까? 이 개념이 여전히 C ++과 같은 언어의 포인터 및 참조와 함께 사용되는 방법이 있습니까? (참고로, 나는 누군가가 이미 참조 투명성의 목표를 가지고 있다고 가정하고있다. 나는 그것이 좋은 목표인지 아닌지에 대해서는 관심이 없다.)
ely

참조를 선호하십시오. 선택이 없을 때의 사용자 포인터.
Ferruccio

답변:


285

내 경험 법칙은 다음과 같습니다.

포인터를 사용하여 포인터 산술을 수행하려는 경우 (예 : 포인터 주소를 배열을 통해 증가시키기 위해) 또는 NULL 포인터를 전달해야하는 경우 포인터를 사용하십시오.

그렇지 않으면 참조를 사용하십시오.


10
포인터가 NULL 인 점이 우수합니다. 포인터 매개 변수가 있으면 NULL이 아닌지 명시 적으로 확인하거나 함수의 모든 사용법을 검색하여 절대 NULL이 아닌지 확인해야합니다. 참고로이 노력은 필요하지 않습니다.
Richard Corden

26
산술의 의미를 설명하십시오. 새로운 사용자는 포인터가 가리키는 것을 조정하려는 것을 이해하지 못할 수 있습니다.
Martin York

7
마틴, 산술적으로 나는 당신이 구조체에 포인터를 전달하지만 그것이 단순한 구조가 아니라 배열이라는 것을 알고 있습니다. 이 경우 []를 사용하여 색인을 생성하거나 포인터에서 ++ /-를 사용하여 산술을 수행 할 수 있습니다. 그것은 요컨대 차이점입니다.
Nils Pipenbrinck

2
마틴, 당신은 포인터로 직접 할 수 있습니다. 참조가 아닙니다. 물론 참조에 대한 포인터를 가지고 실제로 같은 일을 할 수 있지만 그렇게하면 매우 더러운 코드로 끝납니다.
Nils Pipenbrinck

1
다형성 (예 :)은 Base* b = new Derived()어떻습니까? 이것은 포인터없이 처리 할 수없는 경우처럼 보입니다.
Chris Redford

72

코딩 가이드 라인을 호출하는 다음 함수를 설정하면 도움이 될 것이라고 생각합니다.

  1. 다른 모든 장소와 마찬가지로 항상 const정확해야합니다.

    • 참고 : 이는 특히 값을 벗어난 값 (항목 3 참조)과 값을 통해 전달 된 값 (항목 4 참조) 만 const지정자가 부족함을 의미 합니다.
  2. 현재 컨텍스트에서 0 / NULL 값이 유효한 입력 인 경우에만 포인터로 값을 전달하십시오.

    • 근거 1 : 발신자 로서 , 당신은 당신이 전달하는 것이 사용 가능한 상태 에 있어야 한다는 것을 알 수 있습니다.

    • 이유 2 :로는 전화 당신이 들어오는 어떤 것을 알고, 이다 가능한 상태. 따라서 해당 값에 대해 NULL 검사 또는 오류 처리를 수행 할 필요가 없습니다.

    • 이론적 근거 3 : 이론적 근거 1과 2가 컴파일러에 의해 시행 될 것이다 . 가능하면 컴파일 타임에 항상 오류를 잡으십시오.

  3. 함수 인수가 값을 벗어나면 참조로 전달하십시오.

    • 근거 : 우리는 아이템 2를 깨고 싶지 않다 ...
  4. 값이 POD ( Plain old Datastructure )이거나 작거나 (메모리 단위) 또는 다른 방법으로 복사 할 수있을만큼 (시간 단위) 값이 작은 경우에만 "pass by const reference"대신 "value by pass"를 선택하십시오 .

    • 근거 : 불필요한 사본을 피하십시오.
    • 참고 : 작은 충분 하고 저렴한 충분 절대 measurables 수 없습니다.

다음과 같은 경우에는 지침이 없습니다 : "const &를 사용할 때"... 지침 2는 "[in] 값에 대해 작성되어야하며, NULL이 유효한 경우에만 포인터를 통과해야합니다. 그렇지 않으면 const 참조 (또는" 작은 "객체, 복사) 또는 [out] 값인 경우 참조. 잠재적으로 +1을 추가하기 위해이 게시물을 모니터링하고 있습니다.
paercebal

항목 1은 설명하는 사례를 다룹니다.
Johann Gerell

기본 구성 가능하지 않은 경우 매개 변수를 참조로 전달하는 것은 약간 어렵습니다. 그것은 내 코드에서 매우 일반적입니다. 함수가 객체를 생성하는 이유는 사소하지 않기 때문입니다.
MSalters

@MSalters : 함수 내에서 메모리를 할당하려는 경우 (내 생각대로) 할당 된 메모리에 대한 포인터를 반환하지 않는 이유는 무엇입니까?
Kleist

@Kleist : @MSalters를 대신하여 여러 가지 가능한 이유가 있습니다. 하나는 이미 크기 조정 된 것처럼 채울 메모리를 이미 할당했을 수 있다는 것 std::vector<>입니다.
Johann Gerell

24

이것은 결국 주관적입니다. 지금까지의 논의는 유용하지만 이것에 대한 정답이나 결정적인 대답은 없다고 생각합니다. 많은 것은 스타일 가이드 라인과 당시의 요구에 달려 있습니다.

포인터와 함께 몇 가지 다른 기능 (무엇이 NULL이 될 수 있는지 여부)이 있지만 출력 매개 변수의 실질적인 차이점은 순전히 구문입니다. 예를 들어 Google의 C ++ 스타일 가이드 ( https://google.github.io/styleguide/cppguide.html#Reference_Arguments )는 출력 매개 변수에 대한 포인터 만 요구하고 const 인 참조 만 허용합니다. 추론은 가독성 중 하나입니다. 값 구문이있는 것은 포인터 의미 의미를 가져서는 안됩니다. 나는 이것이 반드시 옳고 그름이라고 제안하지는 않지만, 여기서 요점은 그것이 정확성이 아니라 스타일의 문제라는 것입니다.


참조에 값 구문이 있지만 포인터 의미 의미가 있다는 것은 무엇을 의미합니까?
에릭 앤드류 루이스

"참조로 전달"부분은 기능 정의 (값 구문)에서만 명백하기 때문에 사본을 전달하는 것처럼 보이지만 전달한 값을 복사하지 않고 본질적으로 후드 아래에 포인터를 전달합니다. 값을 수정하는 기능.
phant0m 2016 년

구글 C ++ 스타일 가이드가 많이 열등하다는 것을 잊지 말아야한다.
중복 제거기

7

변수 값을 수정하려면 포인터를 전달해야합니다. 기술적으로 참조 또는 포인터를 전달하는 것은 동일하지만 사용 사례에서 포인터를 전달하면 값이 함수에 의해 변경된다는 사실을 "광고"하므로 더 읽기 쉽습니다.


2
Johann Gerell 지침을 따르면, 비 const 참조도 변경 가능한 변수를 광고하므로 포인터가 여기에 이점이 없습니다.
Alexander Kondratskiy

4
@AlexanderKondratskiy : 당신은 ... 당신이 즉시 볼 수없는 점을 놓치고 호출 사이트에서 호출 된 함수는 같은 있었던 파라미터 허용 여부 const또는 비 const참조를,하지만 당신은 매개 변수의이 ALA 통과하면 볼 수 &x대를 x, 사용 매개 변수가 수정되기 쉬운 지 여부를 인코딩하기 위해이 컨버전스. ( const포인터 를 전달하고 싶을 때가있다 . 그래서 확신은 단지 힌트 일 뿐이다. 그렇지 않을 때 무언가를 의심하는 것은 의심 할 여지가 없다고 생각하는 것보다 덜 위험 할 수있다. ....)
Tony Delroy 2016 년

5

값이 없음을 표시해야하는 매개 변수가있는 경우 매개 변수를 포인터 값으로 만들고 NULL로 전달하는 것이 일반적입니다.

대부분의 경우 (안전 측면에서) 더 나은 솔루션은 boost :: optional 을 사용하는 것 입니다. 이를 통해 참조 값과 반환 값으로 선택적 값을 전달할 수 있습니다.

// Sample method using optional as input parameter
void PrintOptional(const boost::optional<std::string>& optional_str)
{
    if (optional_str)
    {
       cout << *optional_str << std::endl;
    }
    else
    {
       cout << "(no string)" << std::endl;
    }
}

// Sample method using optional as return value
boost::optional<int> ReturnOptional(bool return_nothing)
{
    if (return_nothing)
    {
       return boost::optional<int>();
    }

    return boost::optional<int>(42);
}


4

포인터

  • 포인터는 메모리 주소를 보유하는 변수입니다.
  • 포인터 선언은 기본 유형, * 및 변수 이름으로 구성됩니다.
  • 포인터는 일생 동안 여러 변수를 가리킬 수 있습니다
  • 현재 유효한 메모리 위치를 가리 키지 않는 포인터에는 null 값이 제공됩니다 (0 인 경우).

    BaseType* ptrBaseType;
    BaseType objBaseType;
    ptrBaseType = &objBaseType;
  • &는 피연산자의 메모리 주소를 반환하는 단항 연산자입니다.

  • 역 참조 연산자 (*)는 포인터가 가리키는 변수에 저장된 값에 액세스하는 데 사용됩니다.

       int nVar = 7;
       int* ptrVar = &nVar;
       int nVar2 = *ptrVar;

참고

  • 참조 (&)는 기존 변수의 별칭과 같습니다.

  • 참조 (&)는 자동으로 역 참조되는 상수 포인터와 같습니다.

  • 일반적으로 함수 인수 목록 및 함수 반환 값에 사용됩니다.

  • 참조를 만들 때 초기화해야합니다.

  • 객체에 대한 참조가 초기화되면 다른 객체를 참조하도록 변경할 수 없습니다.

  • NULL 참조는 가질 수 없습니다.

  • const 참조는 const int를 참조 할 수 있습니다. const 값을 가진 임시 변수로 수행됩니다.

    int i = 3;    //integer declaration
    int * pi = &i;    //pi points to the integer i
    int& ri = i;    //ri is refers to integer i – creation of reference and initialization

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오


3

참조는 암시 적 포인터입니다. 기본적으로 참조가 가리키는 값을 변경할 수 있지만 다른 것을 가리 키도록 참조를 변경할 수는 없습니다. 따라서 2 센트는 매개 변수의 값만 변경하려면 참조로 전달하지만 다른 객체를 가리 키도록 매개 변수를 변경 해야하는 경우 포인터를 사용하여 매개 변수를 전달해야한다는 것입니다.


3

C #의 out 키워드를 고려하십시오. 컴파일러는 out 키워드를 이미 알고 있지만 out 키워드를 out 인수에 적용하려면 메소드 호출자가 필요합니다. 가독성을 높이기위한 것입니다. 현대의 IDE에서는 구문 (또는 의미 론적) 강조에 대한 작업이라고 생각합니다.


오타 : 시맨틱이 아닌 시맨틱; +1 (C #) 또는 & (C의 경우 참조가 없음)를 쓰지 않고 강조 표시의 가능성에 동의합니다.
peenut

2

전달할 내용을 변경 / 유지하려는 이유가없는 한 const 참조로 전달하십시오.

대부분의 경우 가장 효율적인 방법입니다.

변경하고 싶지 않은 각 매개 변수에 const를 사용해야합니다.이 기능은 함수에서 바보 같은 일을하지 못하게 할뿐만 아니라 다른 사용자에게 전달 된 값에 대한 기능을 다른 사용자에게 잘 보여줍니다. 이것은 당신이 지적한 것을 바꾸고 싶을 때 포인터 const를 만드는 것을 포함합니다 ...


2

포인터 :

  • 할당 할 수 있습니다 nullptr(또는 NULL).
  • 콜 사이트에서 &유형이 포인터 자체가 아닌 경우 객체를 수정하고 명시 적으로 작성해야합니다.
  • 포인터는 리바운드 될 수 있습니다.

참고 문헌 :

  • null 일 수 없습니다.
  • 일단 바인딩되면 변경할 수 없습니다.
  • 발신자는을 명시 적으로 사용할 필요가 없습니다 &. 매개 변수가 수정되었는지 확인하려면 함수 구현으로 이동해야하기 때문에 때로는 나쁜 것으로 간주됩니다.

알지 못하는 사람들에게 작은 점 : nullptr 또는 NULL은 단순히 0입니다. stackoverflow.com/questions/462165/…
Serguei Fedorov

2
nullptr은 0과 동일하지 않습니다. try int a = nullptr; stackoverflow.com/questions/1282295/what-exactly-is-nullptr
Johan Lundberg

0

참조는 참조가 참조하는 값에 액세스하기 위해 접두사 *를 사용할 필요가 없다는 점을 제외하면 포인터와 유사합니다. 또한 초기화 후에 다른 객체를 참조하도록 참조 할 수 없습니다.

참조는 특히 함수 인수를 지정하는 데 유용합니다.

자세한 내용은 "Bjarne Stroustrup"의 "C ++ 둘러보기"(2014) 페이지 11-12를 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.