이것을 다루는 훌륭한 답변이 이미 있습니다. c ++의 Pass-by-reference와 Java의 Pass-by-value 사이의 동작을 대조 하는 매우 간단한 예제 (컴파일 할) 를 공유하여 작은 기여를하고 싶었습니다 .
몇 가지 사항 :
- "참조"라는 용어는 두 가지 의미로 오버로드됩니다. Java에서는 단순히 포인터를 의미하지만 "Pass-by-Preference"의 맥락에서 전달 된 원래 변수에 대한 핸들을 의미합니다.
- 자바는 가치를 전달합니다 입니다. Java는 C의 자손입니다 (다른 언어 중에서도). C 이전에는 FORTRAN 및 COBOL과 같은 일부 (전부는 아님) 언어가 PBR을 지원했지만 C는 지원하지 않았습니다. PBR은 이러한 다른 언어가 서브 루틴 내에서 전달 된 변수를 변경할 수 있도록했습니다. 동일한 기능을 수행하기 위해 (즉, 함수 내 변수 값 변경) C 프로그래머는 변수에 대한 포인터를 함수로 전달했습니다. Java와 같이 C에서 영감을 얻은 언어는이 아이디어를 빌려 왔으며 Java가 포인터를 References라고 부른다는 점을 제외하고 C와 마찬가지로 메소드에 포인터를 계속 전달합니다. 다시, 이것은 "Pass-By-Reference"와 "Reference"라는 단어의 다른 사용입니다.
- C ++에서는 "&"문자 (C 및 C ++에서 "변수의 주소"를 나타내는 데 사용되는 것과 동일한 문자 임)를 사용하여 참조 매개 변수를 선언하여 참조로 전달할 수 있습니다. 예를 들어, 참조로 포인터를 전달하면 매개 변수와 인수가 동일한 객체를 가리키는 것이 아닙니다. 오히려 그들은 같은 변수입니다. 하나가 다른 주소로 설정되거나 null로 설정되면 다른 주소도 설정됩니다.
- 아래의 C ++ 예제 에서 reference 로 null 종료 문자열 에 대한 포인터 를 전달합니다 . 그리고 아래의 Java 예제에서 Java 참조를 문자열 (문자열에 대한 포인터와 동일)에 값으로 전달합니다. 주석의 출력을 확인하십시오.
참조 예제로 C ++ 전달 :
using namespace std;
#include <iostream>
void change (char *&str){ // the '&' makes this a reference parameter
str = NULL;
}
int main()
{
char *str = "not Null";
change(str);
cout<<"str is " << str; // ==>str is <null>
}
값 예제로 Java 전달 "Java 참조"
public class ValueDemo{
public void change (String str){
str = null;
}
public static void main(String []args){
ValueDemo vd = new ValueDemo();
String str = "not null";
vd.change(str);
System.out.println("str is " + str); // ==> str is not null!!
// Note that if "str" was
// passed-by-reference, it
// WOULD BE NULL after the
// call to change().
}
}
편집하다
여러 사람들이 내 예제를 보지 않거나 c ++ 예제를 얻지 못했음을 나타내는 의견을 작성했습니다. 연결이 어디에 있는지 확실하지 않지만 c ++ 예제를 추측하는 것은 분명하지 않습니다. 패스 바이 레퍼런스가 파스칼에서 더 깨끗해 보인다고 생각하기 때문에 파스칼에 동일한 예제를 게시하고 있지만 잘못 될 수 있습니다. 나는 사람들을 더 혼란스럽게 할 수 있습니다. 내가하지 희망.
파스칼에서, 참조에 의해 전달 된 매개 변수를 "var 매개 변수"라고합니다. 아래 setToNil 프로 시저에서 'ptr'매개 변수 앞에 오는 키워드 'var'에 유의하십시오. 이 프로 시저에 포인터가 전달되면 reference 로 전달 됩니다 . 이 절차는 ptr을 nil로 설정하면 (파스칼은 NULL), 인수를 nil로 설정하므로 Java에서는이를 수행 할 수 없습니다.
program passByRefDemo;
type
iptr = ^integer;
var
ptr: iptr;
procedure setToNil(var ptr : iptr);
begin
ptr := nil;
end;
begin
new(ptr);
ptr^ := 10;
setToNil(ptr);
if (ptr = nil) then
writeln('ptr seems to be nil'); { ptr should be nil, so this line will run. }
end.
편집 2
Ken Arnold의 "THE Java Programming Language" , James Gosling (Java를 발명 한 사람) 및 David Holmes의 2 장, 섹션 2.6.5 에서 발췌 한 내용
메소드에 대한 모든 매개 변수는 "값으로"전달됩니다 . 즉, 메소드의 매개 변수 변수 값은 인수로 지정된 호출자의 사본입니다.
그는 객체에 대해 같은 점을 지적합니다. . .
매개 변수가 객체 참조 인 경우 객체 자체가 아니라 "값별"로 전달 되는 것은 객체 참조 입니다.
그리고 같은 섹션의 끝을 향하여 그는 자바가 가치에 의해서만 전달되고 결코 참조에 의해 전달되지 않는다는 것에 대해 더 넓은 진술을합니다.
Java 프로그래밍 언어 는 객체를 참조로 전달하지 않습니다. 그
값에 의해 객체 참조를 전달한다 . 동일한 참조의 두 사본이 동일한 실제 객체를 참조하므로 하나의 참조 변수를 통해 작성된 변경 사항은 다른 참조 변수를 통해 볼 수 있습니다. 값 을 기준으로 mode- pass를 전달하는 매개 변수는 정확히 하나 뿐이며 일을 단순하게 유지하는 데 도움이됩니다.
이 책의 섹션은 Java에서 매개 변수 전달과 참조 별 값과 값별의 차이점에 대한 훌륭한 설명을 제공하며 Java 작성자가 작성합니다. 나는 아직도 당신이 확신하지 않는 경우, 누군가가 그것을 읽을 것을 권장합니다.
두 모델의 차이점은 매우 미묘하다고 생각합니다. 실제로 참조로 전달하는 곳에서 프로그래밍을 수행하지 않으면 두 모델이 다른 곳을 놓치기 쉽습니다.
나는 이것이 논쟁을 해결하기를 희망하지만 아마 그렇지 않을 것입니다.
편집 3
이 게시물에 조금 집착했을 수도 있습니다. 아마도 Java 제작자가 실수로 잘못된 정보를 퍼뜨렸다 고 생각하기 때문일 것입니다. 포인터에 "reference"라는 단어를 사용하는 대신 dingleberry와 같은 다른 것을 사용했다면 아무런 문제가 없었을 것입니다. "자바는 딩글 베리를 참조가 아닌 가치에 따라 전달한다"고 말할 수 있으며, 아무도 혼동하지 않을 것입니다.
이것이 바로 Java 개발자 만이이 문제를 해결하는 이유입니다. 그들은 "참조"라는 단어를보고 그것이 의미하는 바를 정확히 알고 있다고 생각하기 때문에 반대 주장을 고려할 필요조차 없습니다.
어쨌든, 나는 오래된 게시물에서 댓글을 보았습니다. 풍선 비유는 내가 정말 좋아했습니다. 그래서 클립 아트를 붙여서 요점을 설명하기 위해 만화 세트를 만들기로 결심했습니다.
값으로 참조 전달-참조에 대한 변경 사항은 호출자의 범위에 반영되지 않지만 객체의 변경 사항은 반영됩니다. 참조가 복사 되었으나 원본과 복사본이 모두 동일한 개체를 참조하기 때문입니다.
참조로 전달 - 참조 사본이 없습니다. 단일 참조는 호출자와 호출되는 함수 모두에 의해 공유됩니다. 참조 또는 객체 데이터에 대한 변경 사항은 호출자의 범위에 반영됩니다.
편집 4
Java에서 매개 변수 전달의 저수준 구현을 설명하는이 주제에 대한 게시물을 보았습니다. 추상적 인 아이디어를 구체화하기 때문에 위대하고 매우 유용하다고 생각합니다. 그러나 나에게는 문제 의 기술적 구현에 대한 것보다 언어 사양에 설명 된 동작 에 대한 질문이 더 있습니다. 다음은 Java 언어 사양 섹션 8.4.1 에서 발췌 한 것입니다 .
메소드 또는 생성자가 호출되면 (§15.12) 실제 인수 표현식의 값은 메소드 또는 생성자의 본문을 실행하기 전에 선언 된 각 유형으로 새로 작성된 매개 변수를 초기화합니다. DeclaratorId에 나타나는 식별자는 공식 매개 변수를 참조하기 위해 메소드 또는 생성자의 본문에서 간단한 이름으로 사용될 수 있습니다.
즉, java는 메소드를 실행하기 전에 전달 된 매개 변수의 사본을 작성합니다. 대학에서 컴파일러를 공부 대부분의 사람들처럼, 내가 사용하는 "드래곤 도서" 입니다 컴파일러 책. 1 장의 "값별 호출"및 "참조 별 호출"에 대한 자세한 설명이 있습니다. 값별 설명은 Java 스펙과 정확히 일치합니다.
90 년대에 컴파일러를 연구했을 때, 나는 1986 년부터 책의 첫 번째 판을 사용했는데,이 책은 약 9 년에서 10 년 전에 Java를 미리 작성했습니다. 그러나 저는 2007 년부터 제 2 판 의 사본을 보았습니다 . "매개 변수 전달 메커니즘"이라고 라벨이 붙은 1.6.6 절은 매개 변수 전달을 아주 잘 설명합니다. 다음은 Java를 언급 한 "값별 호출"이라는 제목 아래의 발췌문입니다.
값별 호출에서는 실제 매개 변수가 평가되거나 (표현식 인 경우) 복사됩니다 (변수 인 경우). 값은 호출 된 프로 시저의 해당 형식 매개 변수에 속하는 위치에 배치됩니다. 이 메소드는 C 및 Java에서 사용되며 대부분의 다른 언어뿐만 아니라 C ++에서도 일반적인 옵션입니다.