Где хранятся нулевые значения или они хранятся вообще?


39

Я хочу узнать о нулевых значениях или нулевых ссылках.

Например, у меня есть класс под названием Apple, и я создал его экземпляр.

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

Затем я съел это яблоко, и теперь оно должно быть нулевым, поэтому я установил его как нулевое.

myApple = null;

После этого звонка я забыл, что съел его и теперь хочу проверить.

bool isEaten = (myApple == null);

С этим вызовом, куда ссылается myApple? Является ли значение NULL специальным указателем? Если так, если у меня 1000 пустых объектов, занимают ли они 1000 памяти или 1000 int, если мы считаем тип указателя int?

Ответы:


45

В вашем примере myAppleесть специальное значение null(обычно все нулевые биты), и поэтому ничего не ссылается. Объект, на который он первоначально ссылался, теперь теряется в куче. Нет способа узнать его местоположение. Это известно как утечка памяти в системах без сборки мусора.

Если вы изначально установили 1000 ссылок на ноль, то у вас будет место только для 1000 ссылок, обычно 1000 * 4 байта (в 32-битной системе, вдвое больше, чем в 64). Если эти 1000 ссылок изначально указывали на реальные объекты, то вы выделили 1000-кратный размер каждого объекта плюс пространство для 1000 ссылок.

В некоторых языках (например, C и C ++) указатели всегда указывают на что-то, даже когда они неинициализированы. Вопрос в том, является ли адрес, который они имеют, законным для вашей программы. Специальный нулевой адрес (он же null) преднамеренно не отображается в вашем адресном пространстве, поэтому блок управления памятью (MMU) генерирует ошибку сегментации при обращении к ней и сбое вашей программы. Но поскольку нулевой адрес намеренно не отображается, он становится идеальным значением для указания того, что указатель ни на что не указывает, поэтому его роль как null. Для завершения истории, как вы выделяете память с newилиmalloc()операционная система настраивает MMU для отображения страниц оперативной памяти в ваше адресное пространство, и они становятся пригодными для использования. Обычно по-прежнему существуют огромные диапазоны адресного пространства, которые не отображаются, что также приводит к ошибкам сегментации.


Очень хорошее объяснение.
NoChance

6
Это немного неправильно в части «утечки памяти». Это утечка памяти в системах без автоматического управления памятью. Однако GC - не единственный возможный способ реализовать автоматическое управление памятью. C ++ std::shared_ptr<Apple>является примером, который не является ни GC, ни утечкой, Appleкогда обнуляется.
MSalters

1
@MSalters - это не shared_ptrпросто форма для сбора мусора? GC не требует наличия отдельного «сборщика мусора», только сбор мусора.
Восстановите Монику

5
@Brendan: термин «сборка мусора» почти повсеместно понимается как относящийся к недетерминированной сборке, которая происходит независимо от обычного пути кода. Детерминированное разрушение, основанное на подсчете ссылок, является чем-то совершенно другим.
Мейсон Уилер

1
Хорошее объяснение. Одним из немного вводящих в заблуждение предположений является то, что распределение памяти отображается в ОЗУ. ОЗУ является одним из механизмов кратковременного хранения памяти, но фактический механизм хранения абстрагируется от ОС. В Windows (для приложений без нуля) страницы памяти виртуализированы и могут отображаться в ОЗУ, файл подкачки диска или, возможно, другое устройство хранения.
Саймон Гилби

13

Ответ зависит от языка, который вы используете.

C / C ++

В C и C ++ ключевым словом было NULL, а в действительности NULL было 0. Было решено, что «0x0000» никогда не будет действительным указателем на объект, и поэтому это значение присваивается, чтобы указать, что оно неверный указатель Тем не менее, это совершенно произвольно. Если вы попытаетесь получить к нему доступ как указатель, он будет вести себя точно так же, как указатель на объект, которого больше нет в памяти, что приведет к возникновению исключения недопустимого указателя. Сам указатель занимает память, но не более чем целочисленный объект. Следовательно, если у вас есть 1000 нулевых указателей, это эквивалент 1000 целых чисел. Если некоторые из этих указателей указывают на допустимые объекты, то использование памяти будет эквивалентно 1000 целым числам плюс память, содержащаяся в этих допустимых указателях. Помните, что в C или C ++,не означает, что память была освобождена, поэтому вы должны явно удалить этот объект, используя dealloc (C) или delete (C ++).

Джава

В отличие от C и C ++, в Java null является просто ключевым словом. Вместо того, чтобы управлять нулем как указатель на объект, он управляется внутри и обрабатывается как литерал. Это устраняет необходимость связывать указатели как целочисленные типы и позволяет Java полностью абстрагировать указатели. Однако даже если Java скрывает это лучше, они все еще являются указателями, то есть 1000 нулевых указателей по-прежнему потребляют эквивалент 1000 целых чисел. Очевидно, что когда они указывают на объекты, во многом как на C и C ++, эти объекты используют память до тех пор, пока на них не ссылаются указатели, однако, в отличие от C и C ++, сборщик мусора забирает их на следующем проходе и освобождает память, не требуя, чтобы вам приходилось отслеживать, какие объекты освобождены, а какие нет, в большинстве случаев (если, например, у вас нет причин для слабой ссылки на объекты).


9
Ваше различие неверно: фактически, в C и C ++ нулевой указатель вообще не должен указывать на адрес памяти 0 (хотя это естественная реализация, такая же, как в Java и C #). Он может указывать буквально куда угодно. Это слегка смущает тот факт, что литерал-0 может быть неявно преобразован в нулевой указатель. Но битовый шаблон, сохраненный для нулевого указателя, все же не обязательно должен быть всеми нулями.
Конрад Рудольф

1
Нет, ты ошибаешься. Семантика полностью прозрачна ... в программе нулевые указатели и макрос NULL( между прочим, не ключевое слово) обрабатываются так, как если бы они были нулевыми битами. Но они не должны быть реализованы как таковые, а на самом деле некоторые неясные реализации делают использование ненулевых указателей неопределенными. Если я напишу, if (myptr == 0)компилятор сделает правильную вещь, даже если нулевой указатель внутренне представлен как 0xabcdef.
Конрад Рудольф

3
@Neil: константа нулевого указателя (значение целого типа с нулевым значением) может быть преобразована в значение нулевого указателя . (§4.10 C ++ 11.) Не гарантируется, что нулевое значение указателя будет иметь все биты ноль. 0является константой нулевого указателя, но это не означает, что myptr == 0проверяет, все ли биты myptrравны нулю.
Мат

5
@Neil: Вы могли бы хотеть проверить эту запись в C faq или этот вопрос SO
hugomg

1
@Neil Вот почему я старался не упоминать NULLмакрос вообще, а говорил о «нулевом указателе» и явно упомянул, что «литерал-0 может быть неявно преобразован в нулевой указатель».
Конрад Рудольф

5

Указатель - это просто переменная, которая в основном имеет целочисленный тип. Он указывает адрес памяти, где хранится фактический объект.

Большинство языков позволяют получить доступ к членам объекта через эту переменную указателя:

int localInt = myApple.appleInt;

Компилятор знает, как получить доступ к членам Apple. Он «следует» за указателем на myAppleадрес и извлекает значениеappleInt

Если вы присваиваете нулевой указатель переменной указателя, вы делаете указатель указателем на отсутствие адреса памяти. (Что делает доступ участника невозможным.)

Для каждого указателя вам нужна память для хранения целочисленного значения адреса памяти (в основном 4 байта в 32-битных системах, 8 байтов в 64-битных системах). Это также верно для нулевых указателей.


Я думаю, что ссылочные переменные / объекты не совсем указатели. Если вы печатаете их, они содержат ClassName @ Hashcode. JVM внутренне использует Hashtable для хранения Hashcode с фактическим адресом и использует Hash Algorithm для извлечения фактического адреса при необходимости.
minusSeven

@minusSeven Это верно для буквальных объектов, таких как целые числа. В противном случае хеш-таблица содержит указатели на другие объекты, содержащиеся в самом классе Apple.
Нейл

@minusSeven: я согласен. Детали реализации указателя сильно зависят от языка / времени выполнения. Но я думаю, что эти детали не имеют отношения к конкретному вопросу.
Стефан

4

Быстрый пример (обратите внимание, что имена переменных не сохраняются):

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


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

Приветствия.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.