널값은 어디에 저장되거나 전혀 저장되지 않습니까?


39

null 값 또는 null 참조에 대해 배우고 싶습니다.

예를 들어 Apple이라는 클래스가 있고 인스턴스를 만들었습니다.

Apple myApple = new Apple("yummy"); // The data is stored in memory

그런 다음 그 사과를 먹었고 이제는 null이어야합니다. 그래서 나는 null로 설정했습니다.

myApple = null;

이 전화 후에, 나는 그것을 먹었고 지금 확인하고 싶다는 것을 잊었다.

bool isEaten = (myApple == null);

이 전화를 통해 myApple은 어디에 참조됩니까? 널은 특별한 포인터 값입니까? 그렇다면 1000 개의 null 객체가있는 경우 포인터 유형을 int로 생각하면 1000 개의 객체 메모리 공간 또는 1000 개의 int 메모리 공간을 차지합니까?

답변:


45

귀하의 예 myApple에는 특별한 값 null(일반적으로 모든 0 비트)이 있으므로 아무것도 참조하지 않습니다. 원래 참조한 오브젝트는 이제 힙에서 유실됩니다. 위치를 검색 할 방법이 없습니다. 이것은 가비지 수집이없는 시스템에서 메모리 누수로 알려져 있습니다.

원래 1000 개의 참조를 널로 설정 한 경우 1000 개의 참조, 일반적으로 1000 * 4 바이트 (32 비트 시스템에서 64에서 두 배)의 공간이 있습니다. 이러한 1000 개의 참조가 원래 실제 개체를 가리키는 경우 각 개체의 크기에 1000 배를 더한 1000 개의 참조를위한 공간을 할당했습니다.

C 및 C ++와 같은 일부 언어에서는 포인터 가 "초기화되지 않은"경우에도 항상 무언가를 가리 킵니다. 문제는 보유한 주소가 프로그램이 액세스 할 수있는 합법적인지 여부입니다. 특수 주소 0 (일명 null)은 의도적으로 주소 공간에 매핑되지 않으므로 메모리 관리 장치 (MMU)에 액세스하여 프로그램이 충돌하면 세그먼트 오류가 발생합니다. 주소 제로가 의도적으로되어 있기 때문에 그러나 하지 매핑, 그것은 이상적인 값이 포인터, 아무것도 같은 따라서 역할을 가리키는되지 않는다는 것을 나타 내기 위해 사용된다 null. new또는을 사용 하여 메모리를 할당 할 때 스토리를 완료하려면malloc()운영 체제는 RAM 페이지를 주소 공간에 매핑하여 MMU를 사용할 수 있도록 MMU를 구성합니다. 매핑되지 않은 광범위한 주소 공간이 여전히 존재하므로 분할 오류도 발생합니다.


아주 좋은 설명입니다.
NoChance

6
"메모리 누수"부분에서 약간 잘못되었습니다. 자동 메모리 관리가없는 시스템의 메모리 누수입니다. 그러나 GC가 자동 메모리 관리를 구현할 수있는 유일한 방법은 아닙니다. C ++ std::shared_ptr<Apple>는 GC가 아니거나 Apple0 이 되었을 때 누출되지 않는 예입니다 .
MSalters

1
@MSalters- shared_ptr가비지 수집의 기본 형태가 아닙니까? GC는 별도의 "가비지 수집기"가 필요하지 않으며 해당 가비지 수집 만 발생합니다.
Monica Monica 복원

5
@Brendan : "가비지 콜렉션"이라는 용어는 일반 코드 경로와 독립적으로 발생하는 비 결정적 콜렉션을 나타내는 것으로 거의 보편적으로 이해됩니다. 참조 카운트에 기반한 결정 론적 파괴는 완전히 다릅니다.
메이슨 휠러

1
좋은 설명입니다. 약간 오해의 소지가있는 점은 메모리 할당이 RAM에 매핑된다는 가정입니다. RAM은 단기 메모리 저장을위한 메커니즘 중 하나이지만 실제 저장 메커니즘은 OS에 의해 추상화됩니다. Windows에서 (링이 0이 아닌 앱의 경우) 메모리 페이지가 가상화되어 RAM, 디스크 스왑 파일 또는 다른 저장 장치에 매핑 될 수 있습니다.
Simon Gillbee

13

대답은 사용중인 언어에 따라 다릅니다.

C / C ++

C 및 C ++에서 키워드는 NULL이고 실제로 NULL은 0이었습니다. "0x0000"은 객체에 대한 유효한 포인터가 될 수 없다고 결정되었으므로 해당 값은이를 나타내는 것으로 지정됩니다. 유효한 포인터가 아닙니다. 그러나 완전히 임의적입니다. 포인터처럼 액세스하려고 시도하면 더 이상 메모리에 존재하지 않는 객체에 대한 포인터와 똑같이 작동하여 잘못된 포인터 예외가 발생합니다. 포인터 자체는 메모리를 차지하지만 정수 객체보다 많지 않습니다. 따라서 1000 개의 널 포인터가 있으면 1000 개의 정수와 같습니다. 이러한 포인터 중 일부가 유효한 객체를 가리키는 경우 메모리 사용은 1000 개의 정수와 해당 유효한 포인터에 포함 된 메모리에 해당합니다. C 또는 C ++에서는메모리가 해제되었음을 의미 하지 않으므로 dealloc (C) 또는 delete (C ++)를 사용하여 해당 오브젝트를 명시 적으로 삭제해야합니다.

자바

C 및 C ++에서와 달리 Java에서 null은 단순히 키워드입니다. 객체에 대한 포인터처럼 null을 관리하는 대신 내부적으로 관리되고 리터럴처럼 취급됩니다. 따라서 포인터를 정수 유형으로 묶을 필요가 없어지고 Java가 포인터를 완전히 추상화 할 수 있습니다. 그러나 Java가 더 잘 숨기더라도 여전히 포인터이므로 1000 개의 null 포인터는 여전히 1000 개의 정수를 소비합니다. 분명히 C 및 C ++와 같은 객체를 가리킬 때 더 이상 포인터를 참조하지 않을 때까지 해당 객체가 메모리를 사용하지만 C 및 C ++와 달리 가비지 수집기는 다음 단계에서 메모리를 사용하여 메모리를 해제합니다. 예를 들어 객체를 약하게 참조 할 이유가없는 한 대부분의 경우 해제 된 객체와없는 객체를 추적 할 필요가 없습니다.


9
실제로는 C와 C ++에서 널 포인터가 메모리 주소 0을 가리킬 필요가 없습니다 (이것은 Java와 C #에서와 같은 자연스러운 구현이지만). 말 그대로 어디든 가리킬 수 있습니다. 이것은 literal-0이 암시 적으로 널 포인터로 변환 될 수 있다는 사실에 의해 약간 혼란스러워진다. 그러나 널 포인터에 대해 저장된 비트 패턴은 여전히 ​​모두 0 일 필요는 없습니다.
Konrad Rudolph

1
아니요 당신은 틀 렸습니다. 의미는 완전히 투명 ... 프로그램, 널 포인터 및 매크로 NULL( 되지 들이 제로 비트 것처럼로서 키워드) 처리된다. 그러나 그것들은 그렇게 구현 될 필요는 없으며, 실제로는 모호한 구현 에서는 0이 아닌 널 포인터를 사용합니다. 내가 쓰면 if (myptr == 0)null 포인터가 내부적으로로 표시되어 있어도 컴파일러가 올바른 작업을 수행합니다 0xabcdef.
Konrad Rudolph

3
@Neil : 널 포인터 상수 (0으로 평가되는 정수 유형의 값)는 널 포인터 값으로 변환 가능 합니다 . (§4.10 C ++ 11.) 널 포인터 값이 모든 비트를 0으로 보장하지는 않습니다. 0은 널 포인터 상수이지만 myptr == 0모든 비트 myptr가 0 인지 확인하는 것은 아닙니다 .
Mat

5
@Neil : C FAQ 또는 SO 질문 에서이 항목 을 확인하고 싶을 수도 있습니다
hugomg

1
@Neil 그래서 NULL매크로 를 언급하지 않고 "널 포인터"에 대해 이야기하고 "literal-0을 암시 적으로 널 포인터로 암시 적으로 변환 할 수있다"고 명시 적으로 언급하는 데 어려움을 겪었습니다 .
Konrad Rudolph

5

포인터는 단순히 정수형 인 변수입니다. 실제 객체가 저장되는 메모리 주소를 지정합니다.

대부분의 언어는이 포인터 변수를 통해 객체 멤버에 액세스 할 수 있습니다.

int localInt = myApple.appleInt;

컴파일러는의 멤버에 액세스하는 방법을 알고 있습니다 Apple. myApple의 주소에 대한 포인터를 "따르고" 값을 검색합니다.appleInt

널 포인터를 포인터 변수에 지정하면 포인터가 메모리 주소를 가리 키지 않도록합니다. (회원 액세스를 불가능하게 만드는)

모든 포인터에 대해 메모리 주소 정수 값을 보유하려면 메모리가 필요합니다 (대개 32 비트 시스템에서는 4 바이트, 64 비트 시스템에서는 8 바이트). 널 포인터의 경우에도 마찬가지입니다.


참조 변수 / 객체가 정확히 포인터가 아니라고 생각합니다. 인쇄하면 ClassName @ Hashcode가 포함됩니다. JVM은 내부적으로 Hashtable을 사용하여 실제 주소와 함께 Hashcode를 저장하고 필요한 경우 해시 알고리즘을 사용하여 실제 주소를 검색합니다.
마이너스 7

@minusSeven 정수와 같은 리터럴 객체와 관련하여 정확합니다. 그렇지 않으면 해시 테이블은 Apple 클래스 자체에 포함 된 다른 객체에 대한 포인터를 보유합니다.

@minusSeven : 동의합니다. 포인터 구현의 세부 사항은 언어 / 런타임에 따라 크게 다릅니다. 그러나 나는 그 세부 사항이 특정 질문과 관련이 없다고 생각합니다.
Stephan

4

빠른 예 (유형 이름은 저장되지 않음) :

void main()
{
  int X = 3;
  int *Y = X;
  int *Z = null;
} // void main(...)


...........................
....+-----+--------+.......
....|     |   X    |.......
....+-----+--------+.......
....| 100 |   3    |<---+..
....+-----+--------+....|..
........................|..
....+-----+--------+....|..
....|     |   Y    |....|..
....+-----+--------+....|..
....| 102 |  100   +----+..
....+-----+--------+.......
...........................
....+-----+--------+.......
....|     |   z    |.......
....+-----+--------+.......
....| 104 |   0    |.......
....+-----+--------+.......
...........................

건배.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.