Java 참조와 C 포인터는 어떻게 다릅니 까?


97

C에는 포인터가 있고 Java에는 참조라고하는 것이 있습니다. 그들은 모두 무언가를 가리킨다는 점에서 공통점이 있습니다. C의 포인터가 가리키는 주소를 저장한다는 것을 알고 있습니다. 참조도 주소를 저장합니까? 포인터가 더 유연하고 오류가 발생하기 쉽다는 점을 제외하고는 어떻게 다릅니 까?


10
note 참고 C ++에는 포인터 또는 Java 참조
jk

@jk. Java와 동일하다고 생각했습니다. 차이점은 무엇입니까?
Gnijuohz

17
C ++ 참조는 리 바인드 할 수없고 (즉, 지정된 오브젝트를 변경할 수 없음) 널 입력 할 수 없습니다 (즉, 오브젝트를 전혀 참조하지 않는 것이 유효하지 않습니다).
AProgrammer

@Gnijuohz @AProgrammer의 말 : finalJava 의 참조 는 C ++ 참조와 거의 같습니다. 그것들이 정확히 동등 하지 않은 이유 는 C ++ 참조는 추가로 null을 허용하지 않는 반면 finalJava 의 참조는 null을 허용하기 때문입니다.
Utku

답변:


142

주소를 저장하여 참조를 구현할 수 있습니다. 일반적으로 Java 참조는 포인터로 구현되지만 사양에는 필요하지 않습니다. 가비지 수집을 쉽게하기 위해 추가 간접 계층을 사용하고있을 수 있습니다. 그러나 결국 (거의 항상) (Java 스타일) 참조 구현에 관여하는 (C 스타일) 포인터로 요약됩니다.

참조를 사용하여 포인터 산술을 수행 할 수 없습니다. C의 포인터와 Java의 참조 사이의 가장 중요한 차이점은 Java에서 참조의 기본 값을 실제로 얻을 수없고 조작 할 수 없다는 것입니다. 즉, 포인터 산술을 수행 할 수 없습니다.

C에서는 포인터 (예 : 주소)에 무언가를 추가하거나 "가까운"것을 가리 키거나 어떤 장소 에 있는 것을 가리킬 수 있습니다 .

Java에서 참조는 한 가지만 지적합니다. 변수에 다른 참조를 붙일 수는 있지만 "원래의 사물 뒤에있는 것"을 가리 키도록 요청할 수는 없습니다.

참조는 강력하게 입력됩니다. 또 다른 차이점은 참조의 유형이된다는 것이다 많은 당신이있을 수 있습니다 포인터의 타입은 C에서 C로보다 더 엄격하게 자바로 제어 int*하고 그것을 던져 char*그냥 그 위치에 메모리를 재 해석한다. 이 해석은 Java에서 작동하지 않습니다. 참조의 다른 쪽 끝에있는 객체를 이미 존재 하는 것으로 만 해석 할 수 있습니다 (예 : 객체가 실제로 객체 인 경우에만Object 참조에 String대한 참조를 캐스트 할 수 있음 ).String

이러한 차이점으로 인해 C 포인터가 더 강력 해지지 만 더 위험 해집니다. 이 두 가지 가능성 (포인터 산술 및 지정된 값을 재 해석)은 C에 유연성을 추가하고 언어의 일부 힘의 원천입니다. 그러나 잘못 사용하면 코드가 작성되었다는 가정을 쉽게 깨뜨릴 수 있기 때문에 문제의 큰 원인 이기도 합니다. 그리고 그것들을 잘못 사용하는 것은 꽤 쉽습니다.


18
+1 . 구현 세부 사항에 의존하지 마십시오.
CVn

2
+1 가비지 수집은 특정한 대담한 점으로 언급 할 가치가 없습니까? C 포인터가 더 강력하지만 더 위험한 또 다른 방법입니다 (메모리 손상으로 인해 메모리가 손상되어 메모리 누수 위험이 발생할 수 있음)
MarkJ

참조와 포인터의 또 다른 차이점은 C의 포인터가 일련의 숫자로 변환 될 수 있다는 것입니다 (예 :를 memcpy로 이동 하여 사용 char[]). 포인터가 어딘가에 저장된 일련의 숫자로 변환되면 (화면에 표시되고 조작자가 종이에 기록한 경우) 컴퓨터 내의 포인터 사본이 모두 파기되고 해당 숫자의 순서가 변환됩니다 포인터로 돌아 가면 (아마도 연산자로 입력 한 후) 포인터는 여전히 이전과 동일한 것을 가리켜 야합니다. ...
supercat

... 포인터를 숫자로 표시 한 다음 수동으로 입력 한 숫자를 포인터로 변환하면 "악"일 수 있지만, 연산자가 유효한 형식으로 표시되지 않은 숫자를 입력하지 않으면 정의되지 않은 동작이 호출되지 않습니다 바늘. 완전 휴대용 C에서는 범용 가비지 수집이 불가능합니다. 왜냐하면 컴퓨터가 포인터 사본이 우주 어딘가에 존재하는지 알 수 없기 때문입니다.
supercat

JLS §4.3.1에 따르면, Java의 참조는 포인터이므로 "일반적으로 Java 참조는 포인터로 구현되지만 사양에서는 필요하지 않습니다." 거짓입니다.
Lew Bloch

8

C ++ 참조는 다시 다릅니다.

그것들은 초기화되어야하며 null이 될 수 없으며 (적어도 잘 구성된 프로그램에는 없을 수 있음) 다른 것을 참조하기 위해 다시 시드 될 수 없습니다. C ++ 참조는 객체의 별칭과 훨씬 비슷합니다.

포인터와 Java / C ++ 참조의 또 다른 중요한 차이점은 참조 주소에 액세스 할 수없는 포인터의 주소를 사용할 수 있다는 것입니다 (실제로 C ++ 참조는 실제로 메모리에 객체로 존재할 필요는 없음). 포인터에 대한 포인터이지만 참조에 대한 참조는 아닙니다.


4

Java References와 C 포인터는 정확히 두 가지 점이 다릅니다.

  1. 전자에 대한 포인터 산술은 없습니다.
  2. 또한 원하는대로 Java 참조를 만들 수 없으며 액세스 가능한 위치 (정적 필드, 객체 필드, 로컬 변수) 또는 함수 호출 (생성자 호출과 같은)에 의해 반환 된 것만 복사 할 수 있습니다. 객체 (절대 참조와 같은 기본 유형에, char, int등등).

누군가 컴파일러가 int*a 를로 취급하도록 강요 할 수 없기 때문에 참조가 강력하게 형식화되었다고 썼습니다 char*. 특정 변환이 실제로 안전
하다는 사실을 완전히 제외 하고 C에는 다형성이 없으므로 비교는 스타터가 아닙니다. 확실히, Java는 C보다 강력하게 형식화되어 있습니다 .C 포인터 대 Java 참조의 기능이 아니라 JNI를 사용하여 형식 안전을 깨야합니다 (일반 제한을 무시하지 말고) .C에서도 강제 해야합니다. 컴파일러

누군가 Java 참조가 C 포인터로 구현 될 수 있다고 썼는데 , JVM이 C로 구현 된 경우 32 비트 머신에서 엄격하게 덜 강력하기 때문에 일반적으로 강력한 32 비트 머신에서 필자가 말할 것 입니다. 64 비트 시스템에서는 일반적으로 공간과 대역폭을 절약하기 위해 압축 된 일반 객체 포인터 ( "압축 OOP")입니다.
어쨌든, 이러한 C 포인터는 일반적으로 (구현의 99 % 이상) 성능상의 이유로 하드웨어 주소와 동일 할 필요는 없습니다.
마지막으로, 이는 프로그래머에게 노출되지 않은 구현 세부 사항입니다.


-1

그들은 약간 다릅니다. Java에서는 참조 사본이 호출 된 함수의 스택에 복사되어 호출 함수와 동일한 오브젝트를 가리키고 해당 오브젝트를 조작 할 수 있습니다. 그러나 호출 함수가 참조하는 객체는 변경할 수 없습니다.

다음 자바 코드를 고려하십시오

public static void changeRValue(StringBuffer sb){
    sb = new StringBuffer("helllllo"); /*attempt to assign the reference
                                        to a new object*/
}
public static void main(String[] args) {
    StringBuffer sb = new StringBuffer("hi");     //Create a new string buffer
    changeRValue(sb);                             //Call changeRValue
    System.out.println(sb.toString());            //Prints "hi" not "hello"
}

이제 C ++에서 포인터를 고려하십시오.

void func(Dog* dog){
    *dog = Dog("hello world"); //Change the value of dog to a new object
}

int main(int argc, const char * argv[]) {
    Dog dog1("hi");                            //Create a dog object
    func(&dog1);                               //pass the address of dog
    cout << dog1.name;                         //Prints "hello world" not hi.
    return 0;
}

나는 그것이 const Dog *와 유사하게 보일 수 있다고 덧붙일 것이라고 생각했다.
Eladian

2
C ++에서와 동일한 방법으로 Java에서 지정된 오브젝트를 수정할 수 있습니다. 올바른 회원에게만 액세스하면됩니다. Java는 사용자 정의 연산자 오버로드를 지원하지 않기 때문에 할당 된 연산자가 없습니다. 따라서 오버로드 된 연산자를 사용할 수 없습니다. Java의 부족은 Java 참조가 포인터 산술의 C ++ 포인터와 동일하다는 사실과 관련이 없습니다.
중복 제거기
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.