문제

새로운 프로그래밍 언어를 배울 때 직면할 수 있는 장애물 중 하나는 해당 언어가 기본적으로 다음과 같은지 여부에 대한 질문입니다. 값으로 전달 또는 참조로 전달.

그래서 여러분이 가장 좋아하는 언어로 여러분 모두에게 질문을 드립니다. 어떻게 실제로 이루어졌나요?그리고 무엇입니까? 가능한 함정?

물론 당신이 가장 좋아하는 언어는 당신이 사용해 본 적이 있는 언어일 수 있습니다. 인기 있는, 모호, 난해한, 새로운, 오래된...

올바른 솔루션이 없습니다

다른 팁

여기에 내가 기여한 내용이 있습니다. 자바 프로그래밍 언어.

먼저 일부 코드:

public void swap(int x, int y)
{
  int tmp = x;
  x = y;
  y = tmp;
}

이 메서드를 호출하면 다음과 같은 결과가 발생합니다.

int pi = 3;
int everything = 42;

swap(pi, everything);

System.out.println("pi: " + pi);
System.out.println("everything: " + everything);

"Output:
pi: 3
everything: 42"

'실제' 객체를 사용해도 비슷한 결과가 표시됩니다.

public class MyObj {
    private String msg;
    private int number;

    //getters and setters
    public String getMsg() {
        return this.msg;
    }


    public void setMsg(String msg) {
        this.msg = msg;
    }


    public int getNumber() {
        return this.number;
    }


    public void setNumber(int number) {
        this.number = number;
    }

    //constructor
    public MyObj(String msg, int number) {
        setMsg(msg);
        setNumber(number);
    }
}

public static void swap(MyObj x, MyObj y)
{
    MyObj tmp = x;
    x = y;
    y = tmp;
}

public static void main(String args[]) {
    MyObj x = new MyObj("Hello world", 1);
    MyObj y = new MyObj("Goodbye Cruel World", -1); 

    swap(x, y);

    System.out.println(x.getMsg() + " -- "+  x.getNumber());
    System.out.println(y.getMsg() + " -- "+  y.getNumber());
}


"Output:
Hello world -- 1
Goodbye Cruel World -- -1"

따라서 Java가 매개변수를 전달하는 것이 분명합니다. 가치로, 값으로 파이 그리고 모든 것 그리고 MyObj 객체 교환되지 않습니다."가치 기준"이 유일한 방법 Java에서는 매개변수를 메소드에 전달합니다.(예를 들어 C++와 같은 언어에서는 개발자가 '를 사용하여 참조로 매개변수를 전달할 수 있습니다.&' 매개변수 유형 뒤)

지금은 까다로운 부분, 또는 적어도 대부분의 새로운 Java 개발자를 혼란스럽게 할 부분:(에서 빌린 것 자바월드)
원저자:토니 신테스

public void tricky(Point arg1, Point arg2)
{
    arg1.x = 100;
    arg1.y = 100;
    Point temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}
public static void main(String [] args)
{
    Point pnt1 = new Point(0,0);
    Point pnt2 = new Point(0,0);
    System.out.println("X: " + pnt1.x + " Y: " +pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);
    System.out.println(" ");
    tricky(pnt1,pnt2);
    System.out.println("X: " + pnt1.x + " Y:" + pnt1.y); 
    System.out.println("X: " + pnt2.x + " Y: " +pnt2.y);  
}


"Output
X: 0 Y: 0
X: 0 Y: 0
X: 100 Y: 100
X: 0 Y: 0"

교활한 pnt1의 값을 성공적으로 변경했습니다!이는 객체가 참조로 전달된다는 것을 의미하지만, 그렇지 않습니다!올바른 설명은 다음과 같습니다. 그만큼 객체 참조 값으로 전달됩니다.

Tony Sintes의 추가 정보:

이 방법은 PNT1의 값을 성공적으로 변경하더라도 값으로 전달됩니다.그러나 Pnt1 및 Pnt2의 스왑이 실패합니다!이것이 주요 혼란의 원천입니다.Main () 메소드에서 Pnt1 및 Pnt2는 객체 참조에 지나지 않습니다.Pnt1과 Pnt2를 Tricky () 메소드로 전달하면 Java는 다른 매개 변수와 마찬가지로 값으로 참조를 전달합니다.이는 메소드에 전달 된 참조가 실제로 원래 참조의 사본임을 의미합니다.아래 그림 1은 Java가 메소드에 객체를 전달한 후 동일한 객체를 가리키는 두 개의 참조를 보여줍니다.

figure 1
(원천: javaworld.com)

결론 또는 짧게 말하면:

  • Java는 매개변수를 전달합니다. 가치로
  • "가치로"유일한 방법 Java에서 메소드에 매개변수를 전달하기 위해
  • 사용하여 객체의 메소드 매개변수로 주어진 바뀔 것이다 참조로서의 객체는 원래 객체를 가리킵니다.(해당 메소드 자체가 일부 값을 변경하는 경우)

유용한 링크:

여기에 대한 또 다른 기사가 있습니다. C# 프로그래밍 언어

C#은 인수를 전달합니다. 가치로 (기본적으로)

private void swap(string a, string b) {
  string tmp = a;
  a = b;
  b = tmp;
}

따라서 이 버전의 swap을 호출하면 결과가 없습니다.

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: foo
y: bar"

하지만, 자바와 달리 씨# 하다 개발자에게 매개변수를 전달할 기회를 제공 참고로, 이는 매개변수 유형 앞에 'ref' 키워드를 사용하여 수행됩니다.

private void swap(ref string a, ref string b) {
  string tmp = a;
  a = b;
  b = tmp;
} 

이 교환 ~ 할 것이다 참조된 매개변수의 값을 변경합니다.

string x = "foo";
string y = "bar";
swap(x, y);

"output: 
x: bar
y: foo"

C#에는 out 키워드, ref와 out의 차이는 미묘합니다.msdn에서:

메소드를 호출하는 호출자 출력 매개변수 통화 전에 전달 된 변수에 할당 할 필요는 없습니다.그러나 호출 수신자는 반환하기 전에 아웃 매개 변수에 할당해야합니다.

그리고

대조적으로 참조 매개변수 ~이다 처음에 할당된 것으로 간주됨 칼리에 의해.따라서 수신자는 심판에 할당 할 필요가 없습니다사용하기 전에 매개 변수.REF 매개 변수는 방법으로 안팎으로 전달됩니다.

작은 함정은 Java와 마찬가지로 값으로 전달된 객체는 여전히 내부 메서드를 사용하여 변경할 수 있습니다.

결론:

  • C#은 기본적으로 매개변수를 전달합니다. 가치로
  • 하지만 필요한 경우 매개변수를 전달할 수도 있습니다. 참고로 ref 키워드를 사용하여
  • 값으로 전달된 매개변수의 내부 메소드 바뀔 것이다 객체(해당 메소드 자체가 일부 값을 변경하는 경우)

유용한 링크:

파이썬 값별 전달을 사용하지만 이러한 값은 모두 개체 참조이므로 최종 효과는 참조별 전달과 유사합니다.그러나 Python 프로그래머는 객체 유형이 다음과 같은지에 대해 더 많이 생각합니다. 변하기 쉬운 또는 불변.가변 객체(예: 사전, 목록, 사용자 정의 객체)는 내부에서 변경할 수 있는 반면, 불변 객체(예: 정수, 문자열, 튜플)는 변경할 수 없습니다.

다음 예에서는 변경할 수 없는 문자열과 변경 가능한 목록이라는 두 개의 인수가 전달되는 함수를 보여줍니다.

>>> def do_something(a, b):
...     a = "Red"
...     b.append("Blue")
... 
>>> a = "Yellow"
>>> b = ["Black", "Burgundy"]
>>> do_something(a, b)
>>> print a, b
Yellow ['Black', 'Burgundy', 'Blue']

라인 a = "Red" 단지 지역 이름을 생성할 뿐이고, a, 문자열 값의 경우 "Red" 전달된 인수(현재는 숨겨져 있음)에는 영향을 미치지 않습니다. a 이후부터는 현지 이름을 참조해야 합니다).인수가 변경 가능하거나 불변인지 여부에 관계없이 할당은 내부 작업이 아닙니다.

그만큼 b 매개변수는 변경 가능한 목록 객체에 대한 참조이며 .append() 메소드는 목록의 내부 확장을 수행하고 새 항목을 추가합니다. "Blue" 문자열 값.

(문자열 객체는 변경할 수 없기 때문에 내부 수정을 지원하는 메서드가 없습니다.)

함수가 반환되면 a 아무런 효과가 없었고, 연장 기간은 b 참조별 전달 스타일 호출 의미를 명확하게 보여줍니다.

앞서 언급한 바와 같이, 주장이 있더라도 a 변경 가능한 유형이고 함수 내 재할당은 내부 작업이 아니므로 전달된 인수 값이 변경되지 않습니다.

>>> a = ["Purple", "Violet"]
>>> do_something(a, b)
>>> print a, b
['Purple', 'Violet'] ['Black', 'Burgundy', 'Blue', 'Blue']

호출된 함수에 의해 목록이 수정되는 것을 원하지 않으면 대신 불변 튜플 유형(대괄호 대신 리터럴 형식의 괄호로 식별됨)을 사용합니다. .append() 방법:

>>> a = "Yellow"
>>> b = ("Black", "Burgundy")
>>> do_something(a, b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in do_something
AttributeError: 'tuple' object has no attribute 'append'

아직 Perl 답변을 보지 못했기 때문에 하나 작성해야겠다고 생각했습니다.

내부적으로 Perl은 참조에 의한 전달로 효과적으로 작동합니다.함수 호출 인수인 변수는 참조적으로 전달되고, 상수는 읽기 전용 값으로 전달되며, 표현식 결과는 임시로 전달됩니다.목록 할당을 통해 인수 목록을 구성하는 일반적인 관용어 @_, 또는 shift 값에 의한 전달처럼 보이도록 사용자에게 이를 숨기는 경향이 있습니다.

sub incr {
  my ( $x ) = @_;
  $x++;
}

my $value = 1;
incr($value);
say "Value is now $value";

이것은 인쇄됩니다 Value is now 1 왜냐하면 $x++ 내에서 선언된 어휘 변수를 증가시켰습니다. incr() 전달된 변수가 아닌 함수입니다.이 값별 전달 스타일은 일반적으로 대부분의 경우 원하는 방식입니다. 인수를 수정하는 함수는 Perl에서는 드물기 때문에 이 스타일은 피해야 합니다.

그러나 어떤 이유로 이 동작이 특별히 필요한 경우에는 다음 요소에 직접 작업하여 수행할 수 있습니다. @_ 배열은 함수에 전달되는 변수의 별칭이 되기 때문입니다.

sub incr {
  $_[0]++;
}

my $value = 1;
incr($value);
say "Value is now $value";

이번에는 인쇄해보겠습니다. Value is now 2, 왜냐하면 $_[0]++ 표현식이 실제를 증가시켰습니다. $value 변하기 쉬운.이것이 작동하는 방식은 후드 아래에서입니다. @_ 대부분의 다른 배열과 같은 실제 배열이 아닙니다(예: my @array), 대신 해당 요소는 함수 호출에 전달된 인수에서 직접 구축됩니다.이를 통해 필요한 경우 참조별 전달 의미 체계를 구성할 수 있습니다.일반 변수인 함수 호출 인수는 이 배열에 그대로 삽입되고, 상수나 더 복잡한 표현식의 결과는 읽기 전용 임시 항목으로 삽입됩니다.

그러나 실제로는 Perl이 참조 값을 지원하기 때문에 이를 수행하는 경우가 매우 드뭅니다.즉, 다른 변수를 참조하는 값입니다.일반적으로 해당 변수에 대한 참조를 전달하여 변수에 대한 명백한 부작용이 있는 함수를 구성하는 것이 훨씬 더 명확합니다.이는 호출 사이트의 독자에게 참조별 전달 의미 체계가 적용된다는 명확한 표시입니다.

sub incr_ref {
  my ( $ref ) = @_;
  $$ref++;
}

my $value = 1;
incr(\$value);
say "Value is now $value";

여기서는 \ 연산자는 다음과 거의 같은 방식으로 참조를 생성합니다. & C의 주소 연산자

거기에 여기 설명이 좋다 .NET용.

많은 사람들이 참조 개체가 실제로 값으로 전달된다는 사실에 놀랐습니다(C#과 Java 모두에서).스택 주소의 복사본입니다.이렇게 하면 메서드가 개체가 실제로 가리키는 위치를 변경하는 것을 방지할 수 있지만 메서드가 개체의 값을 변경할 수는 있습니다.C#에서는 참조로 참조를 전달할 수 있습니다. 즉, 실제 개체가 가리키는 위치를 변경할 수 있습니다.

있다는 것도 잊지 마세요 이름으로 지나가다, 그리고 값-결과로 전달.

값에 의한 결과 전달은 값에 의한 전달과 유사하며 값이 매개변수로 전달된 원래 변수에 설정된다는 측면이 추가되었습니다.전역 변수와의 간섭을 어느 정도 피할 수 있습니다.참조에 의한 전달이 페이지 오류를 일으킬 수 있는 분할된 메모리에서 분명히 더 좋습니다(참조).

이름으로 전달이란 값이 프로시저 시작 시가 아니라 실제로 사용될 때만 계산된다는 의미입니다.Algol은 이름별 전달을 사용했지만 흥미로운 부작용은 스왑 절차를 작성하기가 매우 어렵다는 것입니다(참조).또한 이름으로 전달된 표현식은 액세스될 때마다 재평가되며 이로 인해 부작용이 발생할 수도 있습니다.

가치로

  • 시스템이 매개변수를 복사해야 하므로 참조보다 느립니다.
  • 입력에만 사용

참고로

  • 포인터만 전달되므로 더 빠릅니다.
  • 입력에 사용 그리고 산출
  • 전역 변수와 함께 사용하면 매우 위험할 수 있습니다.

값별 전달 또는 참조별 전달로 말하는 내용은 언어 전체에서 일관되어야 합니다.언어 전반에 걸쳐 사용되는 가장 일반적이고 일관된 정의는 참조별 전달을 사용하면 "일반적으로" 함수에 변수를 전달할 수 있다는 것입니다(예:명시적으로 주소나 이와 유사한 것을 취하지 않고) 함수는 할당하다 (내용을 변경하지 않음) 함수 내에서 매개변수를 변경하면 호출 범위의 변수에 할당하는 것과 동일한 효과가 있습니다.

이 관점에서 언어는 다음과 같이 그룹화됩니다.각 그룹은 동일한 전달 의미를 갖습니다.두 언어를 같은 그룹에 넣어서는 안 된다고 생각하신다면, 두 언어를 구별하는 예를 생각해 보시기 바랍니다.

포함한 대부분의 언어는 , 자바, 파이썬, 루비, 자바스크립트, 계획, OCaml, 표준 ML, 가다, 오브젝티브-C, 잡담, 등.모두다 값으로만 전달.포인터 값(일부 언어에서는 이를 "참조"라고 함) 전달은 참조 전달로 간주되지 않습니다.우리는 전달된 것, 즉 포인터에만 관심이 있고 가리키는 것이 아닙니다.

다음과 같은 언어 C++, 씨#, PHP 위의 언어처럼 기본적으로 값에 의한 전달이지만 함수는 다음을 사용하여 매개변수를 참조에 의한 전달로 명시적으로 선언할 수 있습니다. & 또는 ref.

항상 참조로 전달됩니다.그러나 실제로 사람들은 거의 항상 값을 얻은 후에 값을 복사하므로 값을 전달하는 방식으로 사용합니다.

에 관하여 제이, 값으로 전달하는 AFAIK만 있는 반면, 많은 양의 데이터를 이동할 수 있는 참조로 전달하는 형태가 있습니다.간단히 로캘이라는 것을 동사(또는 함수)에 전달하면 됩니다.클래스의 인스턴스일 수도 있고 일반 컨테이너일 수도 있습니다.

spaceused=: [: 7!:5 <
exectime =: 6!:2
big_chunk_of_data =. i. 1000 1000 100
passbyvalue =: 3 : 0
    $ y
    ''
)
locale =. cocreate''
big_chunk_of_data__locale =. big_chunk_of_data
passbyreference =: 3 : 0
    l =. y
    $ big_chunk_of_data__l
    ''
)
exectime 'passbyvalue big_chunk_of_data'
   0.00205586720663967
exectime 'passbyreference locale'
   8.57957102144893e_6

명백한 단점은 호출된 함수에서 어떤 방식으로든 변수 이름을 알아야 한다는 것입니다.하지만 이 기술을 사용하면 많은 양의 데이터를 쉽게 이동할 수 있습니다.그렇기 때문에 기술적으로는 참조로 전달되지 않지만 "거의 그런 것"이라고 부릅니다.

PHP도 값으로 전달됩니다.

<?php
class Holder {
    private $value;

    public function __construct($value) {
        $this->value = $value;
    }

    public function getValue() {
        return $this->value;
    }
}

function swap($x, $y) {
    $tmp = $x;
    $x = $y;
    $y = $tmp;
}

$a = new Holder('a');
$b = new Holder('b');
swap($a, $b);

echo $a->getValue() . ", " . $b->getValue() . "\n";

출력:

a b

그러나 PHP4에서는 객체가 다음과 같이 취급되었습니다. 기초 요소.이는 다음을 의미합니다.

<?php
$myData = new Holder('this should be replaced');

function replaceWithGreeting($holder) {
    $myData->setValue('hello');
}

replaceWithGreeting($myData);
echo $myData->getValue(); // Prints out "this should be replaced"

기본적으로 ANSI/ISO C는 둘 중 하나를 사용합니다. 이는 함수와 해당 매개변수를 선언하는 방법에 따라 다릅니다.

함수 매개변수를 포인터로 선언하면 함수는 참조에 의한 전달이 되고, 함수 매개변수를 포인터가 아닌 변수로 선언하면 함수는 값에 의한 전달이 됩니다.

void swap(int *x, int *y);   //< Declared as pass-by-reference.
void swap(int x, int y);     //< Declared as pass-by-value (and probably doesn't do anything useful.)

해당 함수 내에서 생성된 비정적 변수에 대한 포인터를 반환하는 함수를 생성하면 문제가 발생할 수 있습니다.다음 코드의 반환 값은 정의되지 않습니다. 즉, 함수에서 생성된 임시 변수에 할당된 메모리 공간이 덮어쓰기되었는지 여부를 알 수 있는 방법이 없습니다.

float *FtoC(float temp)
{
    float c;
    c = (temp-32)*9/5;
    return &c;
}

그러나 정적 변수에 대한 참조나 매개변수 목록에 전달된 포인터를 반환할 수 있습니다.

float *FtoC(float *temp)
{
    *temp = (*temp-32)*9/5;
    return temp;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top