참조로 전달하는 것과 값으로 전달하는 것의 차이점은 무엇입니까?


답변:


1079

무엇보다도 CS 이론에 정의 된 "값별 통과 대 기준 별 통과"구별은 이제 더 이상 사용되지 않습니다. 원래 "참조 별 통과"로 정의 된 기술은 선호도가 떨어지고 거의 사용 되지 않기 때문 입니다. 1

최신 언어 2 는 다른 (그러나 유사한) 기술 쌍을 사용하여 혼동의 주요 원인 인 동일한 효과 (아래 참조) 를 달성하는 경향이 있습니다 .

이차적 인 혼란의 원천은 "참조에 의한 통과"에서 "참조"가 일반적인 용어 "참조"보다 더 좁은 의미를 갖는다 는 사실이다 (구가 이전에 있기 때문에).


이제 진정한 정의는 다음과 같습니다.

  • 매개 변수가 reference전달 되면 호출자와 수신자 는 매개 변수동일한 변수사용합니다 . 수신자가 매개 변수를 수정하면 효과가 발신자의 변수에 표시됩니다.

  • value 가 매개 변수를 전달 하면 호출자와 수신자는 동일한 값을 가진 두 개의 독립 변수 를 갖습니다. 수신자가 매개 변수를 수정하면 효과가 발신자에게 보이지 않습니다.

이 정의에서 참고할 사항은 다음과 같습니다.

  • 여기서 "가변"은 호출자 (로컬 또는 글로벌) 변수 자체를 의미합니다. 즉, 로컬 변수를 참조로 전달하고 할당하면 호출자 변수 자체를 변경합니다. .

    • 이것은 현재 나쁜 관행으로 간주됩니다 (암시 적 종속성으로). 따라서 거의 모든 최신 언어는 독점적으로 또는 거의 독점적으로 가치에 따라 전달됩니다. 함수 별 참조는 이제 함수가 둘 이상의 값을 리턴 할 수없는 언어에서 주로 "출력 / 인수 인수"형식으로 사용됩니다.
  • "참조로 전달"에서 "참조"의 의미 . 일반적인 "참조"용어와의 차이점은 이 "참조"가 일시적이고 암시 적이라는 것입니다. 수신자가 기본적으로 얻는 것은 "가변"인데, 이는 원래의 것과 같은 "어떻게"입니다. 이 효과가 달성되는 방식은 관련이 없습니다 (예 : 언어는 일부 구현 세부 사항 (주소, 포인터, 역 참조)을 모두 노출 할 수 있음)은 모두 관련이 없습니다. 순 효과가 이것이라면 참조에 의한 것입니다.


이제 현대 언어에서 변수는 "참조 유형" ( "참조로 전달"이후에 발명되고 그에서 영감을 얻은 또 다른 개념) 인 경향이 있습니다 . 즉, 실제 객체 데이터는 어딘가에 (보통 힙에) 별도로 저장됩니다. 오직 "참조"만이 변수로 유지되고 매개 변수로 전달됩니다.

변수의 값은 기술적으로 참조 된 개체가 아니라 참조 자체이기 때문에 이러한 참조를 전달하는 것은 값별 전달에 속합니다 . 그러나 프로그램에 대한 순 효과는 값별 또는 참조 별과 동일 할 수 있습니다.

  • 호출자의 변수에서 참조를 가져 와서 인수로 전달한 경우 이는 참조 별 전달과 동일한 효과를 갖습니다. 참조 오브젝트가 수신자에서 변경되면 호출자에게 변경 사항이 표시됩니다.
    • 그러나이 참조를 보유한 변수가 다시 확인되면 해당 개체를 가리키는 것이 중지 되므로이 변수에 대한 추가 작업은 현재 가리키는 모든 것에 영향을줍니다.
  • 값별 전달과 동일한 효과를 얻기 위해 개체의 복사본이 어느 시점에 만들어집니다. 옵션은 다음과 같습니다.
    • 발신자는 통화 전에 개인 사본을 만들고 대신 수신자에게 참조를 제공 할 수 있습니다.
    • 일부 언어에서는 일부 객체 유형이 "불변"합니다. 값을 변경하는 것처럼 보이는 조작은 원래 객체에 영향을주지 않고 완전히 새로운 객체를 생성합니다. 따라서 이러한 유형의 객체를 인수로 전달하면 항상 값을 전달하는 효과가 있습니다. 수신자가 변경이 필요할 경우 언제든 자동으로 사본이 만들어지고 발신자의 객체는 영향을받지 않습니다.
      • 기능적 언어에서 모든 객체는 변경할 수 없습니다.

보시다시피, 이 기술 쌍은 정의 수준의 기술과 거의 동일합니다. 단지 "변수"를 "참조 된 개체"로 바꾸십시오.

동의 된 이름이 없으므로 "값이 참조 인 경우 값별 호출"과 같은 왜곡 된 설명으로 이어집니다. 1975 년, Barbara Liskov는 " 객체 별 통화 "(또는 때때로 " 공유 별 통화 ")라는 용어를 제안 했지만 결코 그럴 수 없었습니다. 또한,이 문구들 중 어느 것도 원래 쌍과 평행을 이루지 않습니다. 과거의 용어가 더 나은 것이 없으면 다시 재사용되어 혼란을 초래 한 것도 당연합니다. 4


참고 : 오랫동안이 답변은 다음과 같이 말했습니다.

웹 페이지를 공유하고 싶다고 가정 해 보겠습니다. URL을 말하면 참조로 전달됩니다. 해당 URL을 사용하여 볼 수있는 것과 동일한 웹 페이지를 볼 수 있습니다. 해당 페이지가 변경되면 둘 다 변경 사항을 볼 수 있습니다. URL을 삭제하면 해당 페이지에 대한 참조가 삭제되므로 실제 페이지 자체는 삭제되지 않습니다.

페이지를 인쇄하고 출력물을 제공하면 가치가 있습니다. 귀하의 페이지는 원본의 연결이 끊어졌습니다. 이후의 변경 사항은 표시되지 않으며 변경 내용 (예 : 인쇄물에 낙서)이 원본 페이지에 표시되지 않습니다. 출력물을 폐기하면 실제로 객체 사본을 폐기 한 것이지만 원래 웹 페이지는 그대로 유지됩니다.

이것은 "참조"의 더 좁은 의미를 제외하고대부분 정확 합니다. 일시적이고 암시 적입니다 (필요하지는 않지만 명시 적 및 / 또는 지속적)는 추가 기능이며 참조 별 통과 의미의 일부가 아닙니다. 위에서 설명한대로). 더 가까운 비유는 문서의 사본을 제공하고 원본 작업을 권유하는 것입니다.


1 Fortran 또는 Visual Basic으로 프로그래밍하지 않는 한 기본 동작이 아니며 대부분의 현대 언어에서는 참조에 의한 실제 호출이 불가능합니다.

2 상당한 양의 노인도 그것을지지합니다

3 현대의 여러 언어에서 모든 유형은 참조 유형입니다. 이 접근 방식은 1975 년 CLU 언어에 의해 개척되었으며 이후 Python 및 Ruby를 포함한 다른 많은 언어에서 채택되었습니다. 그리고 더 많은 언어가 하이브리드 접근 방식을 사용하는데, 일부 유형은 "값 유형"이고 다른 유형은 "참조 유형"입니다. 그 중 C #, Java 및 JavaScript가 있습니다.

4 오래된 용어 자체 를 재활용하는 것은 나쁘지 않지만 매번 어떤 의미가 사용되는지 분명하게 밝혀야합니다. 그렇게하지 않으면 정확히 혼란을 야기하는 것입니다.


저는 개인적 으로 새로운 기술에 대해 "신규" 또는 "간접"값별 / 참조 별 이라는 용어를 사용합니다 .
ivan_pozdeev

귀하가 제공하는 "정확한"정의는 거의 모든 입문 프로그래밍 과정에서 제공되는 정의가 아닙니다. 구글은 참조로 전달되며 그 대답을 얻지 못할 것입니다. 당신이 제공하는 진정한 정의는 참조를 잘못 사용하는 것입니다. 그 정의를 따를 때 참조가 아닌 별칭을 사용하고 있습니다. 실제로는 동일한 변수 인 두 개의 변수가 있습니다. 당신의 진정한 정의는 아무런 이유없이 대량의 혼란을 야기합니다. 참조로 전달은 주소 전달을 의미한다고 말하면됩니다. 그것은 의미가 있고이 무의미한 혼란을 피할 것입니다.
YungGun

@YungGun 1) "거의 모든 초급 프로그래밍 과정에서 제공되는 정의"링크를 제공하십시오. 또한 이것은 CS 과정이 작성된 10 년에서 3 년 전의 현실이 아니라 오늘날의 현실에서 명확하게하는 것을 목표로합니다. 2) "주소"는 가능한 구현에서 의도적으로 추상화하기 때문에 정의에 사용할 수 없습니다. 예를 들어 일부 언어 (Fortran)에는 포인터가 없습니다. 또한 원시 주소를 사용자에게 공개할지 여부가 다릅니다 (VB는 그렇지 않음). 또한 원시 메모리 주소 일 필요는 없습니다. 변수에 연결할 수있는 모든 것이 가능합니다.
ivan_pozdeev

@ivan_podeev 링크가 없습니다. 죄송합니다. 개인적으로 저는 대학에 가서 프로그래밍 부트 캠프를 수강했기 때문에 "거의 모든 입문 과정"이라고 말합니다. 이 과정은 현대적이었습니다 (5 년 전). "원시 주소"는 "포인터"와 동의어입니다 ... 기술적으로 정확할 수도 있지만 (벚꽃 선택 링크에 따라) 사용하는 언어가 대부분의 프로그래머에게는 실용적이지 않고 혼란 스럽습니다. 당신이 그것에 대한 내 전체 생각을 원한다면 나는 3500 단어 블로그 게시물을 작성했습니다 : medium.com/@isaaccway228/…
YungGun

@YungGun "너무 길고 읽지 않았습니다". 한 눈에 대답에 요약 된 혼란을 정확하게 보여줍니다. 참조에 의한 전달은 구현과 무관 한 추상 기술입니다. 정확히 후드에 전달되는 것은 중요하지 않으며 프로그램에 미치는 영향은 중요합니다.
ivan_pozdeev 5

150

함수에 인수를 전달하는 방법입니다. 참조로 전달하면 호출 된 함수의 매개 변수가 호출자의 전달 된 인수 (값이 아니라 ID-변수 자체)와 동일 함을 의미합니다. 값으로 전달은 호출 된 함수의 매개 변수가 호출자의 전달 된 인수의 사본이됨을 의미합니다. 값은 같지만 변수 (ID)는 다릅니다. 따라서 어떤 경우에는 호출 된 함수가 수행 한 매개 변수를 변경하면 전달 된 인수가 변경되고 다른 경우에는 호출 된 함수 (복사본)의 매개 변수 값만 변경됩니다. 빨리 서둘러 :

  • Java는 값별 전달 만 지원합니다. 객체에 대한 참조를 복사 할 때 호출 된 함수의 매개 변수가 동일한 객체를 가리키고 해당 객체의 변경 사항이 호출자에게 표시 되더라도 항상 인수를 복사합니다. 이것이 혼란 스러울 수 있으므로 여기 에 Jon Skeet이 말한 내용이 있습니다.
  • C #은 값으로 전달 및 참조로 전달 ( ref호출자 및 호출 된 함수에서 사용되는 키워드 )을 지원합니다. Jon Skeet도 여기에 대한 좋은 설명이 있습니다 .
  • C ++은 값으로 전달 및 참조로 전달 (호출 함수에서 사용되는 참조 매개 변수 유형)을 지원합니다. 이에 대한 설명은 다음과 같습니다.

코드

내 언어는 C ++이므로 여기서 사용하겠습니다.

// passes a pointer (called reference in java) to an integer
void call_by_value(int *p) { // :1
    p = NULL;
}

// passes an integer
void call_by_value(int p) { // :2
    p = 42;
}

// passes an integer by reference
void call_by_reference(int & p) { // :3
    p = 42;
}

// this is the java style of passing references. NULL is called "null" there.
void call_by_value_special(int *p) { // :4
    *p = 10; // changes what p points to ("what p references" in java)
    // only changes the value of the parameter, but *not* of 
    // the argument passed by the caller. thus it's pass-by-value:
    p = NULL;
}

int main() {
    int value = 10;
    int * pointer = &value;

    call_by_value(pointer); // :1
    assert(pointer == &value); // pointer was copied

    call_by_value(value); // :2
    assert(value == 10); // value was copied

    call_by_reference(value); // :3
    assert(value == 42); // value was passed by reference

    call_by_value_special(pointer); // :4
    // pointer was copied but what pointer references was changed.
    assert(value == 10 && pointer == &value);
}

그리고 Java의 예는 아프지 않습니다.

class Example {
    int value = 0;

    // similar to :4 case in the c++ example
    static void accept_reference(Example e) { // :1
        e.value++; // will change the referenced object
        e = null; // will only change the parameter
    }

    // similar to the :2 case in the c++ example
    static void accept_primitive(int v) { // :2
        v++; // will only change the parameter
    }        

    public static void main(String... args) {
        int value = 0;
        Example ref = new Example(); // reference

        // note what we pass is the reference, not the object. we can't 
        // pass objects. The reference is copied (pass-by-value).
        accept_reference(ref); // :1
        assert ref != null && ref.value == 1;

        // the primitive int variable is copied
        accept_primitive(value); // :2
        assert value == 0;
    }
}

위키 백과

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_value

http://en.wikipedia.org/wiki/Pass_by_reference#Call_by_reference

이 사람은 거의 그것을 못 박습니다 :

http://javadude.com/articles/passbyvalue.htm


9
왜 downvote? 문제가 있거나 오해로 이어지는 경우 의견을 남겨주십시오.
Johannes Schaub-litb

1
내 투표는 아니었지만 사소한 시점 (대통령 토론에서 취한 종류)에 대해서는 "전략"보다는 "전술"에 가깝다고 말하고 싶습니다.
harpo

28
완성도 +1 다운 보트에 대해 걱정하지 마십시오. 사람들은 이상한 이유로 그렇게합니다. 계산기에 대한 의견 질문에서 프로그래머는 계산기를 사용해야한다고 생각하지 않은 사람에 의해 모든 사람들이 다운 투표했습니다! 어쨌든, 나는 당신의 대답이 매우 좋다고 생각했습니다.
Mark Brittingham

1
Skeet의 설명에 대한 링크가 끊어졌습니다.
돈 지향 프로그래머

Skeet의 설명에 대한 링크는 여전히 손상되었습니다.
Rokit

85

여기에서 많은 답변들 (특히 가장 많이 찬성 된 답변)은 "참조에 의한 호출"이 실제로 무엇을 의미하는지 오해하기 때문에 사실 틀린 것입니다. 여기에 문제를 바로 잡으려는 시도가 있습니다.

TL; DR

가장 간단한 용어로 :

  • 값으로 호출은 을 함수 인수로 전달 함을 의미합니다.
  • 참조로 호출은 변수 를 함수 인수로 전달 함을 의미합니다.

은유 적 용어로 :

  • 가치 의한 부름 은 종이에 무언가를 적고 당신에게 건네주는 곳 입니다. URL 일 수도 있고 전쟁과 평화의 완전한 사본 일 수도 있습니다. 그것이 무엇이든간에, 그것은 내가 당신에게 준 종이에 있으며, 이제는 실제로 당신의 종이 입니다. 이제 종이에 낙서를하거나 종이를 사용하여 다른 곳을 찾은 후 무엇이든 피킹 할 수 있습니다.
  • 참고 전화 하면 내가 적어 놓은 노트를 줄 때가 있습니다. 당신은 내 노트에 낙서를 할 수도 있고 (아마도 당신이 원하지 않을 수도 있습니다), 그 후에 나는 당신이 거기에 넣은 낙서와 함께 노트를 보관합니다. 또한, 당신이나 내가 쓴 것에 다른 곳을 찾는 방법에 대한 정보가 있다면, 당신이나 나는 그곳에 가서 그 정보를 가지고 바이올린을 칠 수 있습니다.

"값별 호출"및 "참조 별 호출" 의미 하지 않는 것은

이 두 개념은 참조 유형 개념 (자바에서는 모든 유형이 하위 유형이고 ObjectC #에서는 모든 class유형 임) 또는 C와 같은 포인터 유형 의 개념 (의미 적으로 동등 함 ) 과 완전히 독립적이며 직교한다는 점에 유의하십시오. 단순히 다른 구문으로 Java의 "참조 유형"을 참조하십시오.

참조 유형 의 개념은 URL에 해당합니다. 둘 다 정보 자체이며 다른 정보에 대한 참조 ( 포인터 인 경우)입니다. 서로 다른 위치에 URL 사본을 많이 보유 할 수 있으며 링크 된 웹 사이트는 변경하지 않습니다. 웹 사이트가 업데이트되면 모든 URL 사본은 여전히 ​​업데이트 된 정보로 이어집니다. 반대로, 한 곳에서 URL을 변경해도 다른 URL 사본에는 영향을 미치지 않습니다.

참고 C ++은 "참조"(예를 들어,의 개념을 가지고 int&있다) 하지 자바와 C #처럼 '의 "참조 유형"하지만 입니다 "참조에 의한 호출"처럼. Java 및 C #의 "참조 유형"및 Python의 모든 유형은 C 및 C ++에서 "포인터 유형"(예 :)을 호출하는 것과 같습니다 int*.


여기 더 길고 공식적인 설명이 있습니다.

술어

우선, 몇 가지 중요한 용어를 강조하여 답변을 명확하게하고 단어를 사용할 때 모두 동일한 아이디어를 참조하도록하고 싶습니다. (실제로, 나는 이러한 주제에 대한 혼동의 대부분은 의도 된 의미를 완전히 전달하지 못하는 방식으로 단어를 사용하는 데 기인한다고 생각합니다.)

먼저 함수 선언의 C와 같은 언어로 된 예제가 있습니다.

void foo(int param) {  // line 1
  param += 1;
}

이 함수를 호출하는 예는 다음과 같습니다.

void bar() {
  int arg = 1;  // line 2
  foo(arg);     // line 3
}

이 예제를 사용하여 몇 가지 중요한 용어를 정의하려고합니다.

  • fooA는 기능은 라인 1 (; C와 C 내가 여기에 가지 않을 선언과 정의 사이의 구별을 ++ 자바는 모든 기능의 방법을 주장하지만, 개념은 일반성의 손실없이 동일)에 선언
  • paramA는 형식 매개 변수는 합니다 foo, 또한 라인 1에 선언
  • argA는 변수 , 특히 로컬 변수 함수의 bar선언 라인에 초기화 2
  • arg3 행 의 특정 호출 에 대한 인수 이기도합니다.foo

여기서 구별해야 할 두 가지 중요한 개념이 있습니다. 첫 번째는 변수입니다 .

  • 은 IS 표현식 평가 결과 언어를. 예를 들어 bar위 함수에서 line 뒤에 int arg = 1;표현식 arg값은 1 입니다.
  • 변수 A는 값 용기 . 변수는 변경 가능 (이것은 대부분의 C와 같은 언어에서 기본값 임), 읽기 전용 (예 : Java final또는 C #을 사용하여 선언 readonly) 또는 매우 불변입니다 (예 : C ++ 사용 const).

구별해야 할 다른 중요한 개념 쌍은 매개 변수인수입니다 .

  • 파라미터 (도 불리는 형식 파라미터 ) A는 변수 의 함수 호출시에 호출자에 의해 공급되어야한다.
  • 인수 A는 가치 함수의 호출에 의해 제공되는 기능의 특정 형식 인자를 만족

가치에 의한 전화

에서 값에 의한 호출 , 함수의 형식 매개 변수는 새로 함수 호출에 대해 생성되며, 이는이 초기화됩니다 변수 들이 인수는.

이것은 다른 종류의 변수가 값으로 초기화되는 것과 동일한 방식으로 작동합니다. 예를 들면 다음과 같습니다.

int arg = 1;
int another_variable = arg;

이곳까지 arganother_variable완전히 독립 변수 - 이들 값은 서로 독립적으로 변경할 수있다. 그러나 another_variable선언 된 지점에서 arg보유하고 있는 것과 동일한 값을 보유 하도록 초기화됩니다 1.

이들은 독립 변수이므로 변경 사항 another_variable은 영향을 미치지 않습니다 arg.

int arg = 1;
int another_variable = arg;
another_variable = 2;

assert arg == 1; // true
assert another_variable == 2; // true

이것은 위의 예제 arg와 의 관계와 정확히 동일 param합니다. 대칭을 위해 여기에서 반복하겠습니다.

void foo(int param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

우리가 코드를 이런 식으로 작성한 것과 똑같습니다.

// entering function "bar" here
int arg = 1;
// entering function "foo" here
int param = arg;
param += 1;
// exiting function "foo" here
// exiting function "bar" here

즉, 어떤 정의하는 특성이다 값으로 호출 수단은 (상기 수신자이다 foo이러한 경우)에 수신 된 값을 인수로되지만 자체 별도 갖는 변수 (발신자의 변수와 그 값을 bar본 경우).

위의 은유로 돌아가서, 내가 bar당신이라면 foo, 내가 당신을 부를 때, 나는 당신에게 그 가치가 적힌 종이를 건네 줍니다. 그 종이라고 부릅니다 param. 이 값은 내가 작성한 변수 (내 로컬 변수)에서 작성한 값 의 사본 입니다 arg.

(제외 : 하드웨어와 운영 체제에 따라, 한 함수에서 다른 함수를 호출하는 방법에 대한 다양한 호출 규칙이 있습니다. 호출 규칙은 우리가 종이에 값을 써서 당신에게 건네 주는지를 결정하는 것과 같습니다. , 또는 당신이 내가 쓴 종이가 있거나 우리 두 사람 앞에서 벽에 쓴다면, 이것은 흥미로운 주제이기도하지만이 긴 대답의 범위를 훨씬 넘어서는 것입니다.)

참조로 전화

에서 참조로 호출 , 함수의 형식 매개 변수는 단순히 새 이름 같은 변수 인수로 해당 발신자 공급 장치는.

위의 예로 돌아가서 다음과 같습니다.

// entering function "bar" here
int arg = 1;
// entering function "foo" here
// aha! I note that "param" is just another name for "arg"
arg /* param */ += 1;
// exiting function "foo" here
// exiting function "bar" here

param대한 또 다른 이름 일뿐 arg입니다. 즉, 동일한 변수 이므로 변경 사항 param이 반영됩니다 arg. 이는 참조 별 호출이 값별 호출과 다른 기본적인 방법입니다.

참조에 의한 호출을 지원하는 언어는 거의 없지만 C ++은 다음과 같이 할 수 있습니다.

void foo(int& param) {
  param += 1;
}

void bar() {
  int arg = 1;
  foo(arg);
}

이 경우 param와 같은 을 가지지 않고 arg실제로 arg 다른 이름 으로 만 증가하므로 증가한 bar것을 관찰 할 수 있습니다 arg.

이것은 오늘날 Java, JavaScript, C, Objective-C, Python 또는 거의 모든 다른 인기있는 언어가 작동하는 방식 이 아닙니다 . 이는 해당 언어가 참조로 호출 되지 않고 값으로 호출됨을 의미합니다.

부록 : 객체 공유에 의한 호출

으로 호출 하지만 실제 값이 참조 유형 또는 포인터 유형 인 경우 "값"자체는별로 흥미롭지 않습니다 (예 : C에서는 플랫폼 별 크기의 정수입니다). 흥미로운 것은 그 가치가 지적하는 것 입니다.

해당 참조 유형 (즉, 포인터)이 변경할 수 있는 경우 흥미로운 효과가 있습니다. 포인팅 된 값을 수정할 수 있으며 호출자는 관찰자가 볼 수없는 경우에도 포인팅 된 값의 변경을 관찰 할 수 있습니다 포인터 자체로 변경됩니다.

URL의 비유를 다시 빌리기 위해, 내가 당신에게 관심있는 것이 URL이 아닌 웹 사이트라면 URL 사본 을 웹 사이트에 제공했다는 사실 은 특별히 흥미롭지 않습니다. URL 사본을 스크라이브해도 URL 사본에 영향을 미치지 않는다는 사실은 우리가 신경 쓰는 것이 아니며 실제로 Java 및 Python과 같은 언어에서는 "URL"또는 참조 유형 값이 전혀 수정되지 않으며, 지적한 것만 가능합니다).

Barbara Liskov는 CLU 프로그래밍 언어 (이 의미론을 가짐)를 발명 할 때 기존의 용어 "값별 호출"및 "참조 별 호출"이이 새로운 언어의 의미론을 설명하는 데 특히 유용하지 않다는 것을 깨달았습니다. 그래서 그녀는 새로운 용어 인 발명자에 의한 호출을 발명했습니다 .

기술적으로 가치에 의해 호출되는 언어를 논의 할 때 일반적으로 사용되는 유형이 참조 또는 포인터 유형 (즉, 거의 모든 현대식 명령형, 객체 지향 또는 다중 패러다임 프로그래밍 언어) 인 언어에 대해 논의 할 때 혼동이 훨씬 덜합니다. 단순히 값으로 전화 하거나 참조로 전화하는 것에 대해 이야기하지 마십시오 . 스틱하는 객체 공유로 전화 (또는 단순히 객체에 의해 호출 ) 아무도 혼동되지 않습니다. :-)


더 나은 설명 : 여기서 구별 할 두 가지 중요한 개념이 있습니다. The first is value versus variable. The other important pair of concepts to distinguish is parameter versus argument:
SK Venkat

2
훌륭한 답변입니다. 필자는 참조로 새로운 스토리지를 생성 할 필요가 없다고 덧붙였다. 매개 변수 이름은 원래 저장소 (메모리)를 참조합니다. 감사
drlolly

1
최고의 답변 IMO
Rafael Eyng

59

2 개 용어를 이해하기 전에 반드시 다음 사항을 이해합니다. 모든 물체에는 구별 할 수있는 두 가지가 있습니다.

  • 그 가치.
  • 주소입니다.

당신이 말하면 employee.name = "John"

에 대해 2 가지가 있음을 알고 name있습니다. 값 "John"과 16 진 숫자 인 메모리에서의 위치는 다음과 같습니다 0x7fd5d258dd00.

언어의 아키텍처 또는 객체 의 유형 (클래스, 구조체 등)에 따라 전송 "John"중이거나0x7fd5d258dd00

전달 "John"은 가치에 의한 전달 로 알려져 있습니다. 전달 0x7fd5d258dd00은 참조로 전달하는 것으로 알려져 있습니다. 이 메모리 위치를 가리키는 사람은의 값에 액세스 할 수 있습니다 "John".

이에 대한 자세한 내용은, 내가 대해 읽어보실 것을 추천 포인터를 역 참조 도 및 클래스를 통해 구조체 (값 형식)을 선택하는 이유 (참조 형)


3
즉, 내가 찾던 것입니다. 실제로 설명이 아닌 개념을 찾아야합니다.
Haisum Usman

Java는 항상 가치에 의해 전달됩니다. Java에서 객체에 대한 참조를 전달하는 것은 값을 전달하는 것으로 간주됩니다. 이는 "통과 0x7fd5d258dd00을 참조로 전달하는 것으로 알려져 있습니다."라는 내용과 모순됩니다.
chetan raina

53

예를 들면 다음과 같습니다.

#include <iostream>

void by_val(int arg) { arg += 2; }
void by_ref(int&arg) { arg += 2; }

int main()
{
    int x = 0;
    by_val(x); std::cout << x << std::endl;  // prints 0
    by_ref(x); std::cout << x << std::endl;  // prints 2

    int y = 0;
    by_ref(y); std::cout << y << std::endl;  // prints 2
    by_val(y); std::cout << y << std::endl;  // prints 2
}

1
마지막 줄이 2 대신 0을 인쇄해야하므로 한 가지 문제가 있다고 생각합니다.
Taimoor Changaiz

@TaimoorChangaiz; 어느 "마지막 줄"? 그런데 IRC를 사용할 수 있다면 Freenode에서 ## programming으로 오십시오. 거기에있는 것들을 설명하기가 훨씬 쉬울 것입니다. 내 별명은 "pyon"입니다.
pyon

1
@ EduardoLeón by_val (y); std :: cout << y << std :: endl; // 2
Taimoor Changaiz를

5
@TaimoorChangaiz : 왜 2를 인쇄하지 않습니까? y이전 줄에서 이미 2로 설정되었습니다. 왜 0으로 돌아 갑니까?
pyon

@ EduardoLeón 내 나쁜. 그래, 너가 맞아. 수정 주셔서 감사합니다
Taimoor Changaiz

28

이것을 얻는 가장 간단한 방법은 Excel 파일입니다. 예를 들어 A1과 B1 셀에 5와 2라는 두 개의 숫자가 있고 세 번째 셀에서 합계를 찾고 싶다고 가정 해 봅시다. 두 가지 방법으로이 작업을 수행 할 수 있습니다.

  • 이 셀에 = 5 + 2 를 입력하여 값을 셀 A2전달합니다 . 이 경우 셀 A1 또는 B1의 값이 변경 되더라도 A2의 합은 동일하게 유지됩니다.

  • 또는 = A1 + B1 을 입력 하여 셀 A1 및 B1의 "참조"를 셀 A2전달합니다 . 이 경우 셀 A1 또는 B1의 값이 변경되면 A2의 합계도 변경됩니다.


이것은 다른 모든 답변 중에서 가장 간단하고 가장 좋은 예입니다.
Amit Ray

18

ref로 전달하면 기본적으로 변수에 대한 포인터를 전달합니다. 변수의 사본을 전달하는 값으로 전달하십시오. 기본 사용법에서 이것은 일반적으로 ref에 의한 전달 변수 변경에 대한 호출 방법이며 호출되지 않는 값에 의한 전달을 의미합니다.


12

값으로 전달은 지정한 변수에 저장된 데이터의 COPY를 전송하고 참조로 전달은 변수 자체에 직접 링크를 전송합니다. 따라서 참조로 변수를 전달한 다음 전달한 블록 내에서 변수를 변경하면 원래 변수가 변경됩니다. 단순히 값으로 전달하면 원래 변수는 전달한 블록으로 변경할 수 없지만 호출시 포함 된 내용의 사본을 얻을 수 있습니다.


7

값으로 전달-이 함수는 변수를 복사하고 사본과 함께 작동하므로 원래 변수의 내용은 변경되지 않습니다.

참조로 전달-함수는 원래 변수를 사용합니다. 다른 함수에서 변수를 변경하면 원래 변수도 변경됩니다.

예 (복사 및 사용 / 직접 시도하고 참조하십시오) :

#include <iostream>

using namespace std;

void funct1(int a){ //pass-by-value
    a = 6; //now "a" is 6 only in funct1, but not in main or anywhere else
}
void funct2(int &a){ //pass-by-reference
    a = 7; //now "a" is 7 both in funct2, main and everywhere else it'll be used
}

int main()
{
    int a = 5;

    funct1(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 5
    funct2(a);
    cout<<endl<<"A is currently "<<a<<endl<<endl; //will output 7

    return 0;
}

단순하게 유지하십시오. 텍스트의 벽은 나쁜 습관이 될 수 있습니다.


이것은 매개 변수 값이 변경되었는지 여부를 이해하는 데 실제로 도움이됩니다. 감사합니다!
Kevin Zhao

5

이들 사이의 주요 차이점은 값 유형 변수가 값을 저장한다는 점입니다. 따라서 메소드 호출에서 값 유형 변수를 지정하면 해당 변수 값의 사본이 메소드에 전달됩니다. 참조 유형 변수는 객체에 대한 참조를 저장하므로 참조 유형 변수를 인수로 지정하면 객체를 참조하는 실제 참조의 사본이 메소드에 전달됩니다. 참조 자체가 값으로 전달 되더라도 메소드는 수신 한 참조를 사용하여 원래 객체와 상호 작용하고 가능하면 수정할 수 있습니다. 유사하게, return 문을 통해 메소드로부터 정보를 리턴 할 때, 메소드는 값 유형 변수에 저장된 값의 사본 또는 참조 유형 변수에 저장된 참조의 사본을 리턴합니다. 참조가 리턴되면 호출 메소드는 해당 참조를 사용하여 참조 된 오브젝트와 상호 작용할 수 있습니다. 그래서,

c #에서 호출 된 메서드가 변수를 수정할 수 있도록 참조로 변수를 전달하기 위해 C #은 키워드 ref 및 out을 제공합니다. ref 키워드를 매개 변수 선언에 적용하면 참조에 의해 변수를 메서드에 전달할 수 있습니다. 호출 된 메서드는 호출자의 원래 변수를 수정할 수 있습니다. ref 키워드는 호출 메소드에서 이미 초기화 된 변수에 사용됩니다. 일반적으로 메소드 호출에 초기화되지 않은 변수가 인수로 포함되면 컴파일러에서 오류가 발생합니다. 키워드를 out으로하여 매개 변수를 선행하면 출력 매개 변수가 작성됩니다. 이것은 컴파일러에게 인수가 참조에 의해 호출 된 메소드로 전달되고 호출 된 메소드가 호출자의 원래 변수에 값을 지정 함을 나타냅니다. 메소드가 가능한 모든 실행 경로에서 출력 매개 변수에 값을 지정하지 않으면 컴파일러가 오류를 생성합니다. 또한 컴파일러가 메소드에 인수로 전달 된 초기화되지 않은 변수에 대한 오류 메시지를 생성하지 못하게합니다. 메소드는 return 문을 통해 호출자에게 하나의 값만 리턴 할 수 있지만 여러 출력 (ref 및 / 또는 out) 매개 변수를 지정하여 많은 값을 리턴 할 수 있습니다.

c # 토론 및 예제를 참조하십시오 링크 텍스트


3

예 :

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const &일반적으로 최고입니다. 건설 및 파괴 페널티가 발생하지 않습니다. 참조가 구성되지 않은 경우 인터페이스는 전달 된 데이터를 변경하도록 제안합니다.


2

요컨대, 지나가는 값은 WHAT이며 참조는 지나는 곳입니다.

값이 VAR1 = "Happy Guy!"이면 "Happy Guy!"만 표시됩니다. VAR1이 "Happy Gal!"로 바뀌면 알 수 없습니다. 참조로 전달되고 VAR1이 변경되면 변경됩니다.


2

원래 변수의 값을 함수에 전달한 후 변경하지 않으려면 " pass by value "매개 변수를 사용 하여 함수를 구성해야합니다 .

그러면 함수는 값만 가지지 만 전달 된 변수의 주소는 갖지 않습니다. 변수의 주소가 없으면 함수 내부의 코드는 함수 외부에서 볼 때 변수 값을 변경할 수 없습니다.

그러나 함수 가 외부에서 볼 때 변수 값을 변경 하는 기능을 제공 하려면 pass by reference 를 사용해야 합니다. 값과 주소 (참조)가 모두 전달되어 함수 내에서 사용 가능합니다.


1

값으로 전달은 인수를 사용하여 값을 함수에 전달하는 방법을 의미합니다. 값으로 전달할 때 우리는 지정한 변수에 저장된 데이터를 복사하며 데이터를 복사 할 때 참조로 전달하는 것보다 느립니다. 복사 된 데이터를 변경하면 원본 데이터는 영향을받지 않습니다. nd 참조 나 전달 주소로 변수 자체에 직접 링크를 보냅니다. 또는 변수에 포인터를 전달합니다. 그것은 더 적은 시간이 소비되는 bcse가 빠릅니다.


0

다음은 전달 값-포인터 값-참조 의 차이점을 보여주는 예입니다 .

void swap_by_value(int a, int b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}   
void swap_by_pointer(int *a, int *b){
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}    
void swap_by_reference(int &a, int &b){
    int temp;

    temp = a;
    a = b;
    b = temp;
}

int main(void){
    int arg1 = 1, arg2 = 2;

    swap_by_value(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 1 2

    swap_by_pointer(&arg1, &arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1

    arg1 = 1;                               //reset values
    arg2 = 2;
    swap_by_reference(arg1, arg2);
    cout << arg1 << " " << arg2 << endl;    //prints 2 1
}

"참조로 전달"방법에는 중요한 제한이 있습니다. 매개 변수가 참조전달 된 것으로 선언되면 (& 기호가 앞에 옴) 해당 실제 매개 변수는 변수 여야합니다 .

"값에 의해 전달됨"형식 매개 변수를 참조하는 실제 매개 변수는 일반적으로 표현식 일 수 있으므로 변수뿐만 아니라 리터럴 또는 함수 호출 결과도 사용할 수 있습니다.

이 함수는 변수 이외의 다른 값에 값을 넣을 수 없습니다. 리터럴에 새로운 값을 할당하거나 표현식이 결과를 변경하도록 강요 할 수 없습니다.

추신 : 현재 스레드에서 Dylan Beattie 답변을 확인하여 일반 단어로 설명 할 수 있습니다.


"매개 변수가 [참조로서] 선언 된 경우 해당 실제 매개 변수는 변수 여야합니다"라고 말하지만 일반적으로 사실이 아닙니다. 참조가 임시 (예 : 함수의 반환 값)에 바인딩 된 경우 수명이 참조와 일치하도록 확장됩니다. 자세한 내용은 여기 를 참조 하십시오 .
Chris Hunt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.