language-agnostic - pointer - java reference function




참조 또는 값으로 전달 하시겠습니까? (8)

.NET에 대한 좋은 설명이 있습니다 .

많은 사람들이 참조 객체가 실제로 C #과 Java 모두에서 가치에 의해 전달된다는 사실에 놀랐습니다. 스택 주소의 복사본입니다. 이것은 메소드가 오브젝트가 실제로 가리키는 위치를 변경하지 못하게하지만 메소드가 오브젝트의 값을 변경할 수있게합니다. C #에서는 참조로 참조를 전달할 수 있으므로 실제 개체가 가리키는 위치를 변경할 수 있습니다.

새로운 프로그래밍 언어를 학습 할 때 발생할 수있는 장애물 중 하나는 기본적으로 언어가 값에 따라 전달 되는지 또는 참조에 의해 전달 되는지에 대한 질문입니다.

여기 여러분이 가장 좋아하는 언어로 된 질문이 실제로 어떻게 이루어 집니까? 그리고 가능한 함정은 무엇입니까?

물론 당신이 좋아하는 언어는 당신이 지금까지해온 것 중 하나 일 수 있습니다 : popular , obscure , esoteric , new old ...


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에서는 객체가 primitives 처럼 취급되었습니다. 다음을 의미합니다.

<?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;
}

나는 아직 Perl 답변을 보지 못했기 때문에 내가 대답 할 것이라고 생각했습니다.

후드 아래에서 Perl은 참조 기준으로 효과적으로 작동합니다. 함수 호출 인수 인 변수는 참조로 전달되고 상수는 읽기 전용 값으로 전달되며 표현식 결과는 임시로 전달됩니다. @_ 에서 목록을 지정하거나 shift 하여 인수 목록을 구성하는 일반적인 관용구는 값을 전달하는 @_ 보이게하여 사용자에게이를 숨기는 경향이 있습니다.

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

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

$x++ 가 전달 된 변수가 아닌 incr() 함수 내에 선언 된 어휘 변수를 증가 시켰기 때문에 Value is now 1 인쇄됩니다.이 Value is now 1 스타일은 일반적으로 대부분의 경우 함수로 원하는 것입니다. 그들의 주장을 수정하는 것은 Perl에서는 드물며, 스타일은 피해야한다.

그러나 어떤 이유로 든이 동작이 구체적으로 필요한 경우, 함수에 전달 된 변수의 별명이므로 @_ 배열의 요소에서 직접 조작하여 수행 할 수 있습니다.

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

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

이번에는 $_[0]++ 표현식이 실제 $value 변수를 증가 시켰기 때문에 Value is now 2 입니다. 이것이 작동하는 방식은 후드 내에서 대부분의 다른 배열과 같은 실제 배열이 아니며 (예 : my @array 의해 얻을 수 my @array ) 대신 해당 요소가 함수 호출에 전달 된 인수에서 직접 작성됩니다. 이를 통해 필요한 경우 참조 별 통과 의미를 구성 할 수 있습니다. 일반 변수 인 함수 호출 인수는이 배열에 그대로 삽입되며 더 복잡한 표현식의 상수 또는 결과는 읽기 전용 임시로 삽입됩니다.

그러나 Perl은 참조 값을 지원하기 때문에 실제로이 작업을 수행하는 것은 매우 드 rare니다. 즉, 다른 변수를 참조하는 값입니다. 일반적으로 변수에 대한 참조를 전달하여 변수에 명백한 부작용이있는 함수를 구성하는 것이 훨씬 더 명확합니다. 이는 콜 사이트에서 독자에게 명백한 표시이며, 참조 별 통과 시맨틱이 적용됨을 나타냅니다.

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

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

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


여기 Java 프로그래밍 언어 에 대한 저의 공헌이 있습니다 .

먼저 몇 가지 코드 :

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"

따라서 pi모든 값과 MyObj 객체 가 교환되지 않기 때문에 Java가 value별로 매개 변수 전달한다는 것이 분명합니다. "값별"은 Java에서 매개 변수를 메소드에 전달하는 유일한 방법 입니다. (예를 들어 c ++와 같은 언어를 사용하면 개발자가 매개 변수 유형 뒤에 ' & '를 사용하여 참조로 매개 변수를 전달할 수 있습니다)

이제 까다로운 부분 또는 적어도 새로운 자바 개발자 대부분을 혼란스럽게 할 부분 : ( javaworld 에서 javaworld )
원저자 : Tony Sintes

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가 객체를 메소드에 전달한 후 동일한 객체를 가리키는 두 개의 참조를 보여줍니다.


(출처 : javaworld.com )

결론 또는 짧은 이야기 :

  • Java는 값으로 매개 변수 전달합니다.
  • "값별" 은 Java에서 매개 변수를 메소드에 전달하는 유일한 방법 입니다.
  • 매개 변수로 제공된 오브젝트의 메소드를 사용하면 참조가 원래 오브젝트를 가리키는대로 오브젝트 가 변경 됩니다. (그 방법 자체가 일부 값을 변경하는 경우)

유용한 링크:


J 에 관해서는 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

명백한 단점은 호출 된 함수에서 어떤 식 으로든 변수 이름을 알아야한다는 것입니다. 그러나이 기술은 많은 데이터를 쉽게 이동할 수 있습니다. 이것이 기술적으로 참조로 전달되지는 않지만 "거의 그 정도"라고 부르는 이유입니다.


가치

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

참고로

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

파이썬 은 값으로 전달을 사용하지만 이러한 모든 값은 객체 참조이므로 순 효과는 참조와 유사합니다. 그러나 파이썬 프로그래머는 객체 유형이 변경 가능한지 아닌지에 대해 더 많이 생각합니다. 가변 객체는 내부에서 변경 될 수 있지만 (예 : 사전, 목록, 사용자 정의 객체), 불변 객체는 변경할 수 없습니다 (예 : 정수, 문자열, 튜플).

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

>>> 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" 단순히 문자열 값 "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() 지원하지 않는 불변의 튜플 유형 (대괄호 대신 리터럴 형식으로 괄호로 식별 .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'




pass-by-value