Di mana nilai-nilai nol disimpan, atau mereka disimpan sama sekali?

39

Saya ingin belajar tentang nilai nol atau referensi nol.

Sebagai contoh, saya memiliki kelas yang disebut Apple dan saya membuat contohnya.

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

Lalu saya makan apel itu dan sekarang harus nol, jadi saya menetapkannya sebagai nol.

myApple = null;

Setelah panggilan ini, saya lupa bahwa saya memakannya dan sekarang ingin memeriksanya.

bool isEaten = (myApple == null);

Dengan panggilan ini, di mana referensi myApple? Apakah null nilai penunjuk khusus? Jika demikian, jika saya memiliki 1000 objek nol, apakah mereka menempati ruang memori objek 1000 atau ruang memori int 1000 jika kita berpikir tipe pointer sebagai int?

Mert Akcakaya
sumber

Jawaban:

45

Dalam contoh Anda myApplememiliki nilai khusus null(biasanya semua nol bit), dan begitu juga referensi apa-apa. Objek yang awalnya disebut sekarang hilang di heap. Tidak ada cara untuk mengambil lokasinya. Ini dikenal sebagai kebocoran memori pada sistem tanpa pengumpulan sampah.

Jika Anda awalnya menetapkan 1000 referensi ke nol, maka Anda memiliki ruang untuk hanya 1000 referensi, biasanya 1000 * 4 byte (pada sistem 32-bit, dua kali lipat pada 64). Jika 1000 referensi tersebut awalnya menunjuk ke objek nyata, maka Anda mengalokasikan 1000 kali ukuran masing-masing objek, ditambah ruang untuk 1000 referensi.

Dalam beberapa bahasa (seperti C dan C ++), pointer selalu menunjuk ke sesuatu, bahkan ketika "tidak diinisialisasi". Masalahnya adalah apakah alamat yang mereka pegang itu sah untuk diakses oleh program Anda. Nol alamat khusus (alias null) sengaja tidak dipetakan ke dalam ruang alamat Anda, sehingga kesalahan segmentasi dihasilkan oleh unit manajemen memori (MMU) ketika diakses dan program Anda macet. Tetapi karena alamat nol sengaja tidak dipetakan, itu menjadi nilai ideal untuk digunakan untuk menunjukkan bahwa pointer tidak menunjuk ke apa pun, karenanya perannya sebagai null. Untuk menyelesaikan cerita, saat Anda mengalokasikan memori dengan newataumalloc(), sistem operasi mengkonfigurasi MMU untuk memetakan halaman RAM ke dalam ruang alamat Anda dan itu dapat digunakan. Masih ada rentang ruang alamat yang luas yang tidak dipetakan, dan juga menyebabkan kesalahan segmentasi.

Randall Cook
sumber
Penjelasan yang sangat bagus.
NoChance
6
Itu sedikit salah pada bagian "kebocoran memori". Ini adalah kebocoran memori dalam sistem tanpa manajemen memori otomatis. Namun, GC bukan satu-satunya cara yang mungkin untuk menerapkan manajemen memori otomatis. C ++ std::shared_ptr<Apple>adalah contoh yang bukan GC atau bocor Appleketika memusatkan perhatian.
MSalters
1
@MSalters - Bukankah shared_ptrhanya bentuk dasar untuk pengumpulan sampah? GC tidak mengharuskan ada "pengumpul sampah" yang terpisah, hanya pengumpulan sampah yang terjadi.
Pasang kembali Monica
5
@ Brendan: Istilah "pengumpulan sampah" hampir secara universal dipahami untuk merujuk pada pengumpulan non-deterministik yang terjadi terlepas dari jalur kode normal. Penghancuran deterministik berdasarkan penghitungan referensi adalah sesuatu yang sama sekali berbeda.
Mason Wheeler
1
Penjelasan yang bagus. Satu titik yang sedikit menyesatkan adalah asumsi bahwa alokasi memori dipetakan ke RAM. RAM adalah salah satu mekanisme penyimpanan memori jangka pendek, tetapi mekanisme penyimpanan sebenarnya disarikan oleh OS. Di Windows (untuk aplikasi non-dering-nol) halaman memori divirtualisasi dan dapat dipetakan ke RAM, file swap disk, atau mungkin perangkat penyimpanan lain.
Simon Gillbee
13

Jawabannya tergantung pada bahasa yang Anda gunakan.

C / C ++

Dalam C dan C ++, kata kunci adalah NULL, dan apa sebenarnya NULL adalah 0. Diputuskan bahwa "0x0000" tidak akan pernah menjadi penunjuk yang valid ke objek, dan itulah nilai yang ditugaskan untuk menunjukkan bahwa itu bukan pointer yang valid. Namun, itu sepenuhnya arbitrer. Jika Anda mencoba mengaksesnya seperti pointer, itu akan berperilaku persis seperti pointer ke objek yang tidak lagi ada dalam memori, menyebabkan pengecualian pointer yang tidak valid dibuang. Pointer itu sendiri menempati memori, tetapi tidak lebih dari objek integer. Oleh karena itu, jika Anda memiliki 1000 null pointer, itu setara dengan 1000 integer. Jika beberapa dari pointer tersebut menunjuk ke objek yang valid, maka penggunaan memori akan setara dengan 1000 integer plus memori yang terkandung dalam pointer yang valid. Ingatlah bahwa dalam C atau C ++,tidak menyiratkan memori telah dirilis, jadi Anda harus secara eksplisit menghapus objek itu menggunakan dealloc (C) atau menghapus (C ++).

Jawa

Tidak seperti di C dan C ++, di Java null hanyalah kata kunci. Alih-alih mengelola null seperti pointer ke objek, itu dikelola secara internal dan diperlakukan seperti literal. Ini menghilangkan kebutuhan untuk mengikat pointer sebagai tipe integer dan memungkinkan Java untuk abstrak pointer sepenuhnya. Namun bahkan jika Java menyembunyikannya lebih baik, mereka masih pointer, artinya 1000 pointer nol masih mengkonsumsi setara dengan 1000 integer. Jelas ketika mereka menunjuk ke objek, seperti C dan C ++, memori dikonsumsi oleh objek-objek itu sampai tidak ada lagi petunjuk yang merujuk mereka, namun tidak seperti dalam C dan C ++, pengumpul sampah mengambilnya pada pass berikutnya dan membebaskan memori, tanpa mengharuskan Anda untuk melacak objek apa yang dibebaskan dan objek mana yang tidak, dalam banyak kasus (kecuali jika Anda memiliki alasan untuk referensi objek lemah misalnya).

Neil
sumber
9
Perbedaan Anda tidak benar: pada kenyataannya, di C dan C ++, pointer nol tidak perlu menunjuk ke alamat memori 0 sama sekali (meskipun ini adalah implementasi alami, sama seperti di Jawa dan C #). Ini dapat menunjuk secara harfiah di mana saja. Ini sedikit dikacaukan oleh fakta bahwa literal-0 dapat secara implisit dikonversi menjadi null pointer. Tetapi pola bit yang disimpan untuk penunjuk nol masih tidak harus seluruhnya nol.
Konrad Rudolph
1
Tidak, kamu salah. Semantiknya benar-benar transparan ... dalam program ini, null pointer dan makro NULL( bukan kata kunci, omong-omong) diperlakukan seolah-olah itu nol-bit. Tapi mereka tidak perlu dilaksanakan seperti itu, dan bahkan beberapa implementasi jelas melakukan penggunaan non-nol nol pointer. Jika saya menulis if (myptr == 0)maka kompiler akan melakukan hal yang benar, bahkan jika pointer nol diwakili secara internal oleh 0xabcdef.
Konrad Rudolph
3
@ Neil: konstanta penunjuk nol (nilai awal tipe integer yang mengevaluasi nol) dapat dikonversi ke nilai penunjuk nol . (§4.10 C ++ 11.) Nilai pointer nol tidak dijamin memiliki semua bit nol. 0adalah konstanta penunjuk nol, tetapi ini tidak berarti myptr == 0memeriksa apakah semua bitnya myptrnol.
Mat
5
@Neil: Anda mungkin ingin memeriksa entri ini di C faq atau pertanyaan SO ini
hugomg
1
@ Neil Itu sebabnya saya bersusah payah untuk tidak menyebutkan NULLmakro sama sekali, lebih berbicara tentang "null pointer", dan secara eksplisit menyebutkan bahwa "literal-0 dapat secara implisit dikonversi menjadi null pointer".
Konrad Rudolph
5

Pointer hanyalah sebuah variabel yang sebagian besar merupakan tipe integer. Ini menentukan alamat memori di mana objek sebenarnya disimpan.

Sebagian besar bahasa memungkinkan untuk mengakses anggota objek melalui variabel penunjuk ini:

int localInt = myApple.appleInt;

Kompiler tahu cara mengakses anggota suatu Apple. Ini "mengikuti" pointer ke myApplealamat dan mengambil nilai dariappleInt

Jika Anda menetapkan pointer nol ke variabel pointer, Anda membuat titik pointer ke alamat memori. (Yang membuat akses anggota tidak mungkin.)

Untuk setiap pointer Anda memerlukan memori untuk menyimpan nilai integer alamat memori (kebanyakan 4 Bytes pada sistem 32 bit, 8 byte pada sistem 64 bit). Ini juga berlaku untuk null pointer.

Stephan
sumber
Saya pikir variabel referensi / objek tidak persis petunjuk. Jika Anda mencetaknya, itu berisi ClassName @ Hashcode. JVM secara internal menggunakan Hashtable untuk menyimpan Hashcode dengan alamat aktual dan menggunakan Algoritma Hash untuk mengambil alamat yang sebenarnya bila perlu.
minusSeven
@ minusSeven Itu benar untuk apa yang menyangkut objek literal seperti integer. Kalau tidak, hashtable memegang pointer ke objek lain yang terkandung dalam kelas Apple itu sendiri.
Neil
@ minusSeven: Saya setuju. Detail implementasi pointer sangat bergantung pada bahasa / runtime. Tetapi saya pikir detail-detail itu tidak relevan untuk pertanyaan spesifik.
Stephan
4

Contoh cepat (perhatikan nama varible yang tidak disimpan):

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


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

Tepuk tangan.

umlcat
sumber