Mengapa alamat nol digunakan untuk penunjuk nol?

121

Dalam C (atau C ++ dalam hal ini), pointer menjadi spesial jika memiliki nilai nol: Saya disarankan untuk mengatur pointer ke nol setelah membebaskan memori mereka, karena itu berarti membebaskan pointer lagi tidak berbahaya; ketika saya memanggil malloc itu mengembalikan sebuah pointer dengan nilai nol jika tidak bisa mendapatkan saya memori; Saya menggunakan if (p != 0)sepanjang waktu untuk memastikan petunjuk yang diberikan valid, dll.

Tetapi karena pengalamatan memori dimulai dari 0, bukankah 0 hanya sebagai alamat yang valid seperti yang lainnya? Bagaimana 0 digunakan untuk menangani pointer nol jika itu masalahnya? Mengapa angka negatif bukan nol?


Edit:

Sekelompok jawaban bagus. Saya akan meringkas apa yang telah dikatakan dalam jawaban yang diungkapkan saat pikiran saya sendiri menafsirkannya dan berharap komunitas akan mengoreksi saya jika saya salah paham.

  • Seperti semua hal lain dalam pemrograman, ini adalah abstraksi. Hanya sebuah konstanta, tidak benar-benar terkait dengan alamat 0. C ++ 0x menekankan hal ini dengan menambahkan kata kunci nullptr.

  • Ini bahkan bukan abstraksi alamat, itu adalah konstanta yang ditentukan oleh standar C dan compiler dapat menerjemahkannya ke beberapa nomor lain selama itu memastikan itu tidak pernah sama dengan alamat "sebenarnya", dan sama dengan pointer nol lainnya jika 0 bukan nilai terbaik untuk digunakan untuk platform.

  • Jika ini bukan abstraksi, seperti yang terjadi di masa-masa awal, alamat 0 digunakan oleh sistem dan terlarang bagi pemrogram.

  • Saran nomor negatif saya adalah curah pendapat yang sedikit liar, saya akui. Menggunakan bilangan bulat bertanda untuk alamat sedikit boros jika itu berarti bahwa selain dari penunjuk nol (-1 atau apa pun), ruang nilai dibagi secara merata antara bilangan bulat positif yang membuat alamat valid dan bilangan negatif yang hanya terbuang percuma.

  • Jika ada angka yang selalu dapat diwakili oleh tipe data, itu 0. (Mungkin 1 juga. Saya memikirkan integer satu-bit yang akan menjadi 0 atau 1 jika unsigned, atau hanya bit yang ditandatangani jika ditandatangani, atau dua bit integer yang mana akan menjadi [-2, 1]. Tapi kemudian Anda bisa menggunakan 0 menjadi null dan 1 menjadi satu-satunya byte yang dapat diakses dalam memori.)

Masih ada sesuatu yang belum terselesaikan di benak saya. Pointer pertanyaan Stack Overflow ke alamat tetap tertentu memberi tahu saya bahwa meskipun 0 untuk pointer nol adalah abstraksi, nilai pointer lain belum tentu. Ini mengarahkan saya untuk memposting pertanyaan Stack Overflow lainnya, Dapatkah saya mengakses alamat nol? .

Joel
sumber
11
Anda bisa dengan mudah mengubah if (p != 0)ke if (p)yang merupakan idiom umum di C dan C ++, meskipun Anda harus keluar dari kebiasaan jika Anda mengambil Java.
Paul Tomblin
14
Menghapus sesuatu dua kali berarti kode Anda salah. Saya menyarankan agar tidak menetapkan pointer ke null setelahnya sehingga Anda dapat crash dan memperbaiki masalah, bukan menekannya. Bagaimanapun, Anda membuat kesalahan dengan mengasumsikan alamat adalah bilangan bulat. Ini belum tentu benar, dan 0 hanya mewakili beberapa nilai pointer aktual yang khusus untuk implementasi. Sebuah "alamat negatif" tidak masuk akal, secara konseptual.
GManNickG
6
@GMan: Mungkin ide yang bagus untuk mengatur pointer ke alamat yang akan memaksa crash, seperti 0xDEADBEEF.
Billy ONeal
5
Pertanyaan yang tidak akan pernah mati!
8
@Noah: Intinya disetel ke null -> sembunyikan kesalahan pemrograman, jangan disetel ke null -> temukan kesalahan pemrograman. Saya tidak tahu tentang Anda, tapi saya ingin kode saya benar.
GManNickG

Jawaban:

65

2 poin:

  • hanya nilai konstan 0 dalam kode sumber yang merupakan pointer nol - implementasi compiler dapat menggunakan nilai apa pun yang diinginkan atau dibutuhkan dalam kode yang sedang berjalan. Beberapa platform memiliki nilai penunjuk khusus yang 'tidak valid' yang mungkin digunakan implementasi sebagai penunjuk null. C FAQ memiliki pertanyaan, "Serius, apakah mesin sebenarnya benar-benar menggunakan penunjuk nol bukan nol, atau representasi berbeda untuk penunjuk ke jenis yang berbeda?" , yang menunjukkan beberapa platform yang menggunakan properti 0 ini sebagai pointer nol di sumber C sementara direpresentasikan secara berbeda pada waktu proses. Standar C ++ memiliki catatan yang menjelaskan bahwa mengubah "ekspresi konstan integral dengan nilai nol selalu menghasilkan pointer nol,

  • nilai negatif mungkin dapat digunakan oleh platform sebagai alamat - standar C hanya harus memilih sesuatu untuk digunakan untuk menunjukkan pointer nol, dan nol dipilih. Sejujurnya saya tidak yakin apakah nilai sentinel lain juga dipertimbangkan.

Satu-satunya persyaratan untuk pointer nol adalah:

  • itu dijamin untuk membandingkan tidak sama dengan penunjuk ke objek sebenarnya
  • setiap dua pointer nol akan membandingkan sama (C ++ menyempurnakan ini sehingga ini hanya perlu menahan untuk pointer ke tipe yang sama)
Michael Burr
sumber
12
+1 Saya menduga 0 dipilih hanya karena alasan sejarah. (0 adalah alamat awal dan alamat tidak valid, sebagian besar waktu.) Tentu saja secara umum asumsi seperti itu tidak selalu benar, tetapi 0 berfungsi dengan baik.
GManNickG
8
Ruang mungkin juga menjadi faktor penyebab. Pada hari-hari ketika C pertama kali dikembangkan, memori JAUH lebih mahal daripada sekarang. Angka nol dapat dengan mudah dihitung menggunakan instruksi XOR atau tanpa perlu memuat nilai langsung. Bergantung pada arsitekturnya, ini berpotensi menghemat ruang.
Sparky
6
@GMan - Anda benar. Pada CPU awal, alamat memori nol adalah khusus dan memiliki perlindungan perangkat keras terhadap akses dari perangkat lunak yang berjalan (dalam beberapa kasus ini adalah awal dari vektor reset, dan memodifikasinya dapat mencegah CPU untuk mengatur ulang atau memulai). Pemrogram menggunakan perlindungan perangkat keras ini sebagai bentuk deteksi kesalahan dalam perangkat lunak mereka, membiarkan logika dekode alamat CPU memeriksa petunjuk yang tidak diinisialisasi atau tidak valid daripada harus menggunakan instruksi CPU untuk melakukannya. Konvensi tetap berlaku hingga hari ini, meskipun tujuan alamat nol mungkin telah berubah.
bta
10
Kompiler Minix 16 bit menggunakan 0xFFFF untuk NULL.
Joshua
3
Di banyak sistem tertanam, 0 adalah alamat yang valid. Nilai -1 (semua bit satu) juga merupakan alamat yang valid. Checksum untuk ROM sulit dihitung ketika data dimulai di alamat 0. :-(
Thomas Matthews
31

Secara historis, ruang alamat mulai dari 0 selalu ROM, digunakan untuk beberapa sistem operasi atau rutinitas penanganan interupsi tingkat rendah, saat ini, karena semuanya virtual (termasuk ruang alamat), sistem operasi dapat memetakan alokasi apa pun ke alamat mana pun, sehingga dapat secara khusus TIDAK mengalokasikan apa pun di alamat 0.

Aviad P.
sumber
6
Cukup banyak. Ini berdasarkan konvensi historis, dan alamat pertama digunakan untuk penangan interupsi, sehingga tidak dapat digunakan untuk program normal. Juga, 0 adalah "kosong", yang dapat diartikan sebagai tidak ada nilai / tanpa penunjuk.
TomTom
15

IIRC, nilai "pointer null" tidak dijamin nol. Kompilator menerjemahkan 0 ke dalam nilai "nol" apa pun yang sesuai untuk sistem (yang dalam praktiknya mungkin selalu nol, tetapi tidak harus). Terjemahan yang sama diterapkan setiap kali Anda membandingkan penunjuk dengan nol. Karena Anda hanya dapat membandingkan pointer satu sama lain dan terhadap nilai khusus-0 ini, ini mengisolasi programmer dari mengetahui apa pun tentang representasi memori sistem. Adapun mengapa mereka memilih 0 daripada 42 atau semacamnya, saya akan menebak itu karena sebagian besar pemrogram mulai menghitung pada 0 :) (Juga, pada kebanyakan sistem 0 adalah alamat memori pertama dan mereka ingin itu nyaman, karena dalam terjemahan praktek seperti yang saya gambarkan jarang benar-benar terjadi; bahasa hanya mengizinkannya).

rmeador
sumber
5
@ Justin: Anda salah paham. Konstanta 0 selalu merupakan penunjuk nol. Apa yang dikatakan @meador adalah bahwa mungkin saja pointer nol (ditunjukkan dengan konstanta 0) tidak sesuai dengan alamat nol. Pada beberapa platform, membuat pointer null ( int* p = 0) mungkin membuat pointer yang berisi nilai 0xdeadbeefatau nilai lain yang lebih disukai. 0 adalah penunjuk nol, tetapi penunjuk nol belum tentu penunjuk ke alamat nol. :)
jalf
Pointer NULL adalah nilai yang dicadangkan dan tergantung pada kompilernya bisa berupa pola bit apa pun. Pointer NULL tidak berarti menunjuk ke alamat 0.
Sharjeel Aziz
3
Tapi @Jalf, konstanta 0 tidak selalu merupakan pointer nol. Itulah yang kita tulis ketika kita ingin kompilator mengisi pointer nol aktual platform untuk kita. Secara praktis, penunjuk nol biasanya tidak sesuai dengan alamat nol, dan saya menafsirkan pertanyaan Joel sebagai menanyakan mengapa demikian. Seharusnya ada byte memori yang valid di alamat itu, jadi mengapa tidak menggunakan alamat yang tidak ada dari byte yang tidak ada daripada menghapus byte yang valid dari permainan? (Saya menulis apa yang saya bayangkan Joel pikirkan, bukan pertanyaan yang saya tanyakan pada diri saya sendiri.)
Rob Kennedy
@ Rob: Semacam. Saya tahu apa yang Anda maksud, dan Anda benar, tapi saya juga. :) Bilangan bulat konstan 0 mewakili pointer nol pada tingkat kode sumber. Membandingkan pointer nol dengan 0 menghasilkan nilai true. Menetapkan 0 ke sebuah pointer mengatur pointer itu ke null. 0 adalah penunjuk nol. Tetapi representasi aktual dalam memori dari pointer nol mungkin berbeda dari pola bit nol. (Ngomong-ngomong, komentar saya adalah menanggapi komentar @ Justin yang sekarang telah dihapus, bukan untuk pertanyaan @ Joel. :)
jalf
@jalf @Rob Anda perlu beberapa istilah untuk memperjelas, saya pikir. :) Dari §4.10 / 1: "Sebuah konstanta penunjuk null adalah ekspresi konstan integral rvalue jenis integer yang mengevaluasi ke nol. Konstanta penunjuk null dapat diubah ke jenis penunjuk; hasilnya adalah nilai penunjuk null dari jenis itu dan dapat dibedakan dari setiap nilai pointer ke objek atau pointer ke tipe fungsi. "
GManNickG
15

Anda pasti salah paham tentang arti nol konstan dalam konteks pointer.

Baik di C maupun di C ++ pointer tidak dapat "memiliki nilai nol". Pointer bukanlah objek aritmatika. Mereka tidak boleh memiliki nilai numerik seperti "nol" atau "negatif" atau apa pun yang bersifat seperti itu. Jadi pernyataan Anda tentang "pointer ... memiliki nilai nol" tidak masuk akal.

Dalam C & C ++ pointer dapat memiliki nilai null-pointer yang telah dipesan . Representasi sebenarnya dari nilai null-pointer tidak ada hubungannya dengan "nol". Ini benar-benar bisa apa saja yang sesuai untuk platform tertentu. Memang benar bahwa pada kebanyakan plaform nilai penunjuk-nol direpresentasikan secara fisik oleh nilai alamat nol yang sebenarnya. Namun, jika pada beberapa platform, alamat 0 sebenarnya digunakan untuk beberapa tujuan (yaitu Anda mungkin perlu membuat objek di alamat 0), nilai penunjuk-nol pada platform tersebut kemungkinan besar akan berbeda. Ini bisa secara fisik direpresentasikan sebagai 0xFFFFFFFFnilai alamat atau sebagai0xBAADBAAD nilai alamat, misalnya.

Namun demikian, terlepas dari bagaimana nilai penunjuk-nol direpresentasikan pada platform tertentu, dalam kode Anda, Anda masih akan terus menunjuk penunjuk-nol secara konstan 0. Untuk menetapkan nilai null-pointer ke pointer tertentu, Anda akan terus menggunakan ekspresi seperti p = 0. Merupakan tanggung jawab penyusun untuk mewujudkan apa yang Anda inginkan dan menerjemahkannya ke dalam representasi nilai penunjuk-nol yang tepat, misalnya untuk menerjemahkannya ke dalam kode yang akan memasukkan nilai alamat 0xFFFFFFFFke penunjuk p, misalnya.

Singkatnya, fakta bahwa Anda menggunakan 0kode sorce Anda untuk menghasilkan nilai penunjuk-nol tidak berarti bahwa nilai penunjuk-nol entah bagaimana terikat ke alamat 0. Yang 0Anda gunakan dalam kode sumber Anda hanyalah "gula sintaksis" yang sama sekali tidak ada hubungannya dengan alamat fisik aktual yang menjadi nilai penunjuk-null "menunjuk".

Semut
sumber
3
<quote> Pointer bukanlah objek aritmatika </quote> Pointer aritmatika didefinisikan dengan cukup baik di C dan C ++. Bagian dari persyaratan adalah bahwa kedua pointer menunjuk dalam komposit yang sama. Pointer null tidak menunjuk ke komposit apa pun sehingga menggunakannya dalam ekspresi aritmatika pointer adalah ilegal. Misalnya, tidak ada jaminan itu (p1 - nullptr) - (p2 - nullptr) == (p1 - p2).
Ben Voigt
5
@ Ben Voigt: Spesifikasi bahasa menentukan pengertian jenis aritmatika . Yang saya katakan adalah bahwa tipe pointer tidak termasuk dalam kategori tipe aritmatika. Aritmatika penunjuk adalah cerita yang berbeda dan sama sekali tidak berhubungan, hanya kebetulan linguistik.
ANT
1
Bagaimana seseorang yang membaca benda aritmatika seharusnya mengetahui bahwa itu berarti "dalam arti jenis aritmatika" dan bukan "dalam arti operator aritmatika" (beberapa di antaranya dapat digunakan pada pointer) atau "dalam arti aritmatika penunjuk". Sejauh menyangkut kebetulan linguistik, objek aritmatika memiliki lebih banyak huruf yang sama dengan aritmatika penunjuk daripada tipe aritmatika . Pada saat yang sama, standar berbicara tentang nilai penunjuk . Poster asli mungkin berarti representasi integer dari sebuah pointer daripada nilai pointer , dan NULLsecara eksplisit tidak perlu diwakili oleh 0.
Ben Voigt
Nah, misalnya istilah objek skalar dalam terminologi C / C ++ hanyalah singkatan dari objek berjenis skalar (seperti objek POD). = objek jenis POD ). Saya menggunakan istilah objek aritmatika dengan cara yang persis sama, yang berarti objek dari tipe aritmatika . Saya berharap "seseorang" memahaminya seperti itu. Seseorang yang tidak selalu bisa meminta klarifikasi.
AnT
1
saya bekerja pada sistem di mana (sejauh menyangkut perangkat keras) null adalah 0xffffffff dan 0 adalah alamat yang benar-benar valid
pm100
8

Tetapi karena pengalamatan memori dimulai dari 0, bukankah 0 hanya sebagai alamat yang valid seperti yang lainnya?

Pada beberapa / banyak / semua sistem operasi, alamat memori 0 khusus dalam beberapa hal. Misalnya, sering dipetakan ke memori tidak valid / tidak ada, yang menyebabkan pengecualian jika Anda mencoba mengaksesnya.

Mengapa angka negatif bukan nol?

Saya pikir nilai pointer biasanya diperlakukan sebagai angka unsigned: jika tidak misalnya pointer 32-bit hanya akan mampu mengatasi 2 GB memori, bukan 4 GB.

ChrisW
sumber
4
Saya telah membuat kode pada perangkat di mana alamat nol adalah alamat yang valid, dan tidak ada perlindungan memori. Pointer nol juga semuanya-bit-nol; jika Anda secara tidak sengaja menulis ke pointer nol maka Anda mengecam pengaturan OS yang berada di alamat nol; kegembiraan biasanya tidak terjadi.
MM
1
Ya: pada CPU x86 non-protected-mode, misalnya, alamat 0 adalah tabel vektor interupsi .
ChrisW
@ChrisW: Pada non-protected-mode x86, alamat nol khususnya adalah vektor interupsi divide-by-zero, yang beberapa program mungkin memiliki alasan yang sepenuhnya sah untuk menulis.
supercat
Bahkan pada platform di mana penyimpanan yang dapat digunakan akan dimulai dari alamat fisik, nol, implementasi C dapat dengan mudah menggunakan alamat nol untuk menyimpan objek yang alamatnya tidak pernah diambil, atau membiarkan kata pertama dari memori tidak digunakan. Pada kebanyakan platform, bandingkan-dengan-nol menyimpan instruksi versus bandingkan-dengan-apapun-lainnya, jadi bahkan membuang-buang kata pertama penyimpanan akan lebih murah daripada menggunakan alamat bukan-nol untuk null. Perhatikan bahwa tidak ada persyaratan bahwa alamat hal-hal yang tidak tercakup oleh Standar C (misalnya port I / O atau vektor interupsi) dibandingkan tidak sama dengan nol, atau bahwa ...
supercat
... proses sistem mengakses pointer-nol secara berbeda dari yang lain, jadi semua-bit-nol umumnya merupakan alamat yang bagus untuk "null" bahkan pada sistem di mana akses ke lokasi fisik nol akan berguna dan bermakna.
supercat
5

Dugaan saya adalah bahwa nilai ajaib 0 dipilih untuk menentukan penunjuk yang tidak valid karena dapat diuji dengan instruksi yang lebih sedikit. Beberapa bahasa mesin secara otomatis menyetel tanda nol dan tanda sesuai dengan data saat memuat register sehingga Anda dapat menguji penunjuk null dengan memuat instruksi kemudian dan cabang sederhana tanpa melakukan instruksi perbandingan terpisah.

(Kebanyakan ISA hanya menyetel flag pada instruksi ALU, bukan memuat. Dan biasanya Anda tidak menghasilkan pointer melalui komputasi, kecuali pada compiler saat mengurai sumber C. Tetapi setidaknya Anda tidak memerlukan konstanta lebar pointer yang berubah-ubah untuk bandingkan dengan.)

Di Commodore Pet, Vic20, dan C64 yang merupakan mesin pertama yang saya kerjakan, RAM dimulai di lokasi 0 sehingga benar-benar valid untuk membaca dan menulis menggunakan penunjuk nol jika Anda benar-benar menginginkannya.

KPexEA
sumber
3

Saya pikir itu hanya konvensi. Harus ada beberapa nilai untuk menandai penunjuk yang tidak valid.

Anda baru saja kehilangan satu byte ruang alamat, yang seharusnya jarang menjadi masalah.

Tidak ada petunjuk negatif. Pointer selalu tidak ditandatangani. Juga jika mereka bisa negatif, konvensi Anda berarti Anda kehilangan setengah ruang alamat.

Axel Gneiting
sumber
Catatan: Anda tidak benar-benar kehilangan ruang alamat; Anda bisa mendapatkan pointer ke alamat 0 dengan melakukan: char *p = (char *)1; --p;. Karena perilaku pada penunjuk nol tidak ditentukan oleh standar, sistem ini psebenarnya dapat membaca dan menulis alamat 0, kenaikan untuk memberikan alamat 1, dll.
MM
@MattMcNabb: Implementasi di mana alamat nol adalah alamat perangkat keras yang valid dapat secara sah mendefinisikan perilaku char x = ((char*)0);membaca alamat nol dan menyimpan nilai itu ke dalam x. Kode tersebut akan menghasilkan Perilaku Tidak Terdefinisi pada implementasi apa pun yang tidak menentukan perilakunya, tetapi fakta bahwa standar mengatakan sesuatu adalah Perilaku Tidak Terdefinisi sama sekali tidak melarang implementasi menawarkan spesifikasi mereka sendiri untuk apa yang akan dilakukannya.
supercat
@supercat ITYM *(char *)0. Itu benar, tetapi dalam saran saya, penerapannya tidak perlu mendefinisikan perilaku *(char *)0atau operasi penunjuk null lainnya.
MM
1
@MattMcNabb: Perilaku char *p = (char*)1; --p;hanya akan ditentukan oleh standar jika urutan itu telah dilakukan setelah penunjuk ke sesuatu selain byte pertama dari suatu objek telah dilemparkan ke sebuah intptr_t, dan hasil dari cor itu kebetulan menghasilkan nilai 1 , dan dalam kasus tertentu hasil dari --pakan menghasilkan pointer ke byte sebelum nilai pointer, ketika dilemparkan ke intptr_t, telah menghasilkan 1.
supercat
3

Meskipun C menggunakan 0 untuk merepresentasikan pointer nol, perlu diingat bahwa nilai dari pointer itu sendiri tidak boleh nol. Namun, sebagian besar pemrogram hanya akan menggunakan sistem di mana penunjuk nol sebenarnya adalah 0.

Tapi kenapa nol? Ya, itu satu alamat yang dibagikan setiap sistem. Dan seringkali alamat rendah dicadangkan untuk tujuan sistem operasi sehingga nilainya bekerja dengan baik sebagai terlarang untuk program aplikasi. Penetapan nilai integer yang tidak disengaja ke pointer cenderung berakhir nol seperti yang lainnya.

George Phillips
sumber
3
Alasan yang lebih mungkin di balik semua ini adalah bahwa: murah untuk membagikan memori yang telah diinisialisasi sebelumnya ke nol dan nyaman untuk memiliki nilai dalam memori tersebut yang mewakili sesuatu yang berarti seperti integer 0, floating point 0.0 dan pointer nol. Data statis di C yang diinisialisasi ke nol / null tidak harus menempati ruang apa pun di file yang dapat dieksekusi dan dipetakan ke blok kosong saat dimuat. Nol mungkin mendapatkan perlakuan khusus dalam bahasa mesin juga: perbandingan nol yang mudah seperti "cabang jika sama dengan nol", dll. MIPS bahkan memiliki register tiruan yang hanya konstanta nol.
Kaz
2

Secara historis, memori yang rendah dari suatu aplikasi ditempati oleh sumber daya sistem. Pada masa itulah nol menjadi nilai nol default.

Meskipun ini belum tentu benar untuk sistem modern, itu masih merupakan ide yang buruk untuk menetapkan nilai pointer ke apa pun selain alokasi memori apa yang telah diberikan kepada Anda.

Fred Haslam
sumber
2

Mengenai argumen tentang tidak menyetel pointer ke nol setelah menghapusnya sehingga di masa mendatang menghapus "mengekspos kesalahan" ...

Jika Anda benar-benar khawatir tentang hal ini maka pendekatan yang lebih baik, yang dijamin akan berhasil, adalah dengan memanfaatkan assert ():


...
assert(ptr && "You're deleting this pointer twice, look for a bug?");
delete ptr;
ptr = 0;
...

Ini membutuhkan beberapa pengetikan ekstra, dan satu pemeriksaan ekstra selama debug build, tetapi pasti akan memberi Anda apa yang Anda inginkan: pemberitahuan ketika ptr dihapus 'dua kali'. Alternatif yang diberikan dalam diskusi komentar, tidak menyetel pointer ke null sehingga Anda akan mengalami crash, tidak dijamin akan berhasil. Lebih buruk lagi, tidak seperti di atas, ini dapat menyebabkan crash (atau lebih buruk lagi!) Pada pengguna jika salah satu "bug" ini berhasil masuk ke rak. Terakhir, versi ini memungkinkan Anda terus menjalankan program untuk melihat apa yang sebenarnya terjadi.

Saya menyadari ini tidak menjawab pertanyaan yang diajukan, tetapi saya khawatir seseorang yang membaca komentar mungkin sampai pada kesimpulan bahwa itu dianggap 'praktik yang baik' untuk TIDAK menetapkan petunjuk ke 0 jika mungkin mereka dikirim secara gratis () atau hapus dua kali. Dalam beberapa kasus, jika memungkinkan, JANGAN PERNAH menggunakan praktik yang baik untuk menggunakan Perilaku Tidak Terdefinisi sebagai alat debugging. Tidak seorang pun yang harus memburu bug yang pada akhirnya disebabkan oleh penghapusan penunjuk yang tidak valid akan mengusulkan hal ini. Jenis kesalahan ini membutuhkan waktu berjam-jam untuk memburu dan hampir selalu mempengaruhi program dengan cara yang sama sekali tidak terduga yang sulit untuk tidak mungkin untuk melacak kembali ke masalah aslinya.

Edward Strange
sumber
2

Alasan penting mengapa banyak sistem operasi menggunakan semua-bit-nol untuk representasi penunjuk nol, adalah bahwa ini berarti memset(struct_with_pointers, 0, sizeof struct_with_pointers)dan yang serupa akan mengatur semua penunjuk di dalam struct_with_pointerske penunjuk nol. Ini tidak dijamin oleh standar C, tetapi banyak program yang mengasumsikannya.

zwol
sumber
1

Di salah satu mesin DEC lama (menurut saya PDP-8), runtime C akan melindungi memori halaman pertama memori sehingga setiap upaya untuk mengakses memori di blok itu akan menyebabkan pengecualian untuk dimunculkan.

Paul Tomblin
sumber
PDP-8 tidak memiliki kompiler C. PDP-11 tidak memiliki perlindungan memori dan VAX terkenal karena diam-diam mengembalikan 0 ke referensi penunjuk NULL. Saya tidak yakin mesin mana yang dimaksud.
fuz
1

Pilihan nilai sentinel bersifat arbitrer, dan ini sebenarnya sedang ditangani oleh versi C ++ berikutnya (secara informal dikenal sebagai "C ++ 0x", kemungkinan besar akan dikenal di masa mendatang sebagai ISO C ++ 2011) dengan diperkenalkannya kata kunci -1 untuk beberapa nilai N. Dengan kata lain, alamat biasanya diperlakukan sebagai nilai unsigned. Jika nilai maksimum digunakan sebagai nilai sentinel, maka nilai itu harus bervariasi dari sistem ke sistem tergantung pada ukuran memori sedangkan 0 selalu merupakan alamat yang dapat diwakili. Ini juga digunakan untuk alasan historis, karena alamat memori 0 biasanya tidak dapat digunakan dalam program, dan saat ini sebagian besar OS memiliki bagian kernel yang dimuat ke halaman bawah memori, dan halaman semacam itu biasanya dilindungi sedemikian rupa sehingga jika disentuh (didereferensi) oleh program (simpan kernel) akan menyebabkan kesalahan.nullptr untuk mewakili pointer bernilai null. Dalam C ++, nilai 0 dapat digunakan sebagai ekspresi inisialisasi untuk POD apa pun dan untuk objek apa pun dengan konstruktor default, dan memiliki arti khusus untuk menetapkan nilai sentinel dalam kasus inisialisasi penunjuk. Adapun mengapa nilai negatif tidak dipilih, alamat biasanya berkisar dari 0 hingga 2 N

Michael Aaron Safyan
sumber
1

Itu harus memiliki nilai. Jelas Anda tidak ingin menginjak nilai-nilai yang mungkin secara sah ingin digunakan oleh pengguna. Saya akan berspekulasi bahwa karena runtime C menyediakan segmen BSS untuk data yang diinisialisasi nol, itu masuk akal untuk menafsirkan nol sebagai nilai penunjuk yang tidak diinisialisasi.

JustJeff
sumber
0

Jarang OS memungkinkan Anda untuk menulis ke alamat 0. Adalah umum untuk menyimpan hal-hal khusus OS di memori rendah; yaitu, IDT, tabel halaman, dll. (Tabel harus dalam RAM, dan lebih mudah untuk menempelkannya di bagian bawah daripada mencoba dan menentukan di mana bagian atas RAM.) Dan tidak ada OS yang waras akan membiarkan Anda mengedit tabel sistem mau tidak mau.

Hal ini mungkin tidak ada dalam pikiran K & R ketika mereka membuat C, tetapi (bersama dengan fakta bahwa 0 == null cukup mudah diingat) membuat 0 menjadi pilihan populer.

cHao
sumber
Ini tidak benar dalam mode terlindungi, dan pada kenyataannya, pada konfigurasi Linux tertentu, Anda dapat menulis ke alamat virtual 0.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
0

Nilai 0adalah nilai khusus yang memiliki berbagai arti dalam ekspresi tertentu. Dalam kasus pointer, seperti yang telah ditunjukkan berkali-kali, ini mungkin digunakan karena pada saat itu cara yang paling nyaman untuk mengatakan "masukkan nilai sentinel default di sini". Sebagai ekspresi konstan, ini tidak memiliki arti yang sama dengan bitwise zero (yaitu, semua bit diatur ke nol) dalam konteks ekspresi pointer. Di C ++, ada beberapa tipe yang tidak memiliki representasi bitwise zeroNULL seperti pointer member dan pointer to member function.

Untungnya, C ++ 0x memiliki kata kunci baru untuk "ekspresi yang berarti pointer tidak valid diketahui bahwa tidak juga memetakan ke bitwise nol untuk ekspresi integral": nullptr. Meskipun ada beberapa sistem yang dapat Anda targetkan dengan C ++ yang memungkinkan dereferensi alamat 0 tanpa muntah, jadi berhati-hatilah programmer.

MSN
sumber
0

Sudah ada banyak jawaban bagus di utas ini; mungkin ada banyak alasan berbeda untuk memilih nilai 0untuk pointer nol, tetapi saya akan menambahkan dua lagi:

  • Di C ++, nol-inisialisasi pointer akan mengaturnya ke null.
  • Pada banyak prosesor, lebih efisien untuk menetapkan nilai ke 0 atau mengujinya sama / tidak sama dengan 0 daripada untuk konstanta lainnya.
Mark Ransom
sumber
0

Ini tergantung pada implementasi pointer di C / C ++. Tidak ada alasan khusus mengapa NULL setara dalam penugasan ke sebuah pointer.

Nagabhushan Baddi
sumber
-1

Ada alasan historis untuk ini, tetapi ada juga alasan pengoptimalan untuk itu.

Biasanya OS menyediakan proses dengan halaman memori yang diinisialisasi ke 0. Jika program ingin menafsirkan bagian dari halaman memori itu sebagai penunjuk maka nilainya 0, jadi cukup mudah bagi program untuk menentukan bahwa penunjuk tersebut adalah tidak diinisialisasi. (ini tidak berfungsi dengan baik saat diterapkan ke halaman flash yang tidak diinisialisasi)

Alasan lain adalah bahwa pada banyak prosesor, sangat mudah untuk menguji kesetaraan nilai dengan 0. Terkadang perbandingan gratis dilakukan tanpa memerlukan instruksi tambahan, dan biasanya dapat dilakukan tanpa perlu memberikan nilai nol di register lain atau sebagai literal dalam aliran instruksi untuk dibandingkan.

Perbandingan murah untuk kebanyakan prosesor adalah yang bertanda kurang dari 0, dan sama dengan 0. (bertanda lebih besar dari 0 dan tidak sama dengan 0 disiratkan oleh keduanya)

Karena 1 nilai dari semua nilai yang mungkin perlu disimpan sebagai nilai buruk atau tidak diinisialisasi, maka Anda sebaiknya menjadikannya salah satu yang memiliki pengujian termurah untuk kesetaraan dengan nilai buruk. Ini juga berlaku untuk string karakter yang diakhiri '\ 0'.

Jika Anda mencoba menggunakan lebih besar atau kurang dari 0 untuk tujuan ini, rentang alamat Anda akan terpotong menjadi dua.

nategoose
sumber
-2

Konstan 0digunakan sebagai pengganti NULLkarena C dibuat oleh beberapa triliunan manusia gua dari tahun lalu, NULL, NIL, ZIP, atau NADDAakan memiliki semua dibuat jauh lebih masuk akal daripada 0.

Tetapi karena pengalamatan memori dimulai dari 0, bukankah 0 hanya sebagai alamat yang valid seperti yang lainnya?

Memang. Meskipun banyak sistem operasi yang melarang Anda memetakan apa pun di alamat nol, bahkan dalam ruang alamat virtual (orang menyadari C adalah bahasa yang tidak aman, dan mencerminkan bahwa bug dereferensi penunjuk nol sangat umum, memutuskan untuk "memperbaikinya" dengan tidak mengizinkan kode ruang pengguna untuk dipetakan ke halaman 0; Jadi, jika Anda memanggil panggilan balik tetapi penunjuk panggilan balik adalah NULL, Anda tidak akan mengeksekusi beberapa kode arbitrer).

Bagaimana 0 digunakan untuk menangani pointer nol jika itu masalahnya?

Karena 0digunakan sebagai perbandingan pointer akan diganti dengan beberapa nilai spesifik implementasi , yang merupakan nilai kembali malloc pada kegagalan malloc.

Mengapa angka negatif bukan nol?

Ini akan lebih membingungkan.

L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
sumber
Maksud Anda tentang "manusia gua", dll. Mungkin terletak pada akarnya, meskipun menurut saya spesifikasinya berbeda. Bentuk paling awal dari apa yang berevolusi menjadi C dirancang untuk berjalan pada satu arsitektur tertentu di mana an inttidak hanya berukuran sama dengan penunjuk - dalam banyak konteks, sebuah intdan penunjuk dapat digunakan secara bergantian. Jika sebuah rutin mengharapkan sebuah pointer dan satu dilewatkan dalam sebuah integer 57, rutin tersebut akan menggunakan alamat dengan pola bit yang sama dengan nomor 57. Pada mesin-mesin tersebut, pola bit untuk menunjukkan sebuah pointer null adalah 0, jadi meneruskan int 0 akan melewatkan pointer nol.
supercat
Sejak saat itu, C telah berkembang sehingga dapat digunakan untuk menulis program untuk berbagai macam mesin lain dengan representasi angka dan pointer yang berbeda. Sementara konstanta numerik bukan nol jarang digunakan sebagai pointer, angka nol konstan banyak digunakan untuk merepresentasikan pointer nol. Untuk melarang penggunaan seperti itu akan merusak kode yang ada, jadi compiler diharapkan menerjemahkan angka nol ke dalam implementasi yang digunakan untuk merepresentasikan pointer nol.
supercat
-4

( Harap Baca Paragraf Ini Sebelum Membaca Posting.Saya meminta siapa pun yang tertarik membaca posting ini harus mencoba membacanya dengan cermat, dan tentu saja jangan downvote sampai Anda memahaminya sepenuhnya, terima kasih. )

Sekarang menjadi wiki komunitas, dengan demikian jika seseorang tidak setuju dengan salah satu konsep, harap memodifikasinya, dengan penjelasan yang jelas dan terperinci tentang apa yang salah dan mengapa, dan jika mungkin kutip sumber atau berikan bukti yang dapat direproduksi.

Menjawab

Berikut adalah beberapa alasan lain yang mungkin menjadi faktor yang mendasari NULL == 0

  1. Fakta bahwa nol adalah salah, jadi seseorang dapat melakukan secara langsung if(!my_ptr)daripadaif(my_ptr==NULL) .
  2. Fakta bahwa bilangan bulat global yang belum diinisiasi diinisialisasi secara default ke semua nol, dan dengan demikian penunjuk dari semua nol akan dianggap tidak diinisialisasi.

Di Sini Saya Ingin Mengatakan Sepatah Kata Tentang Jawaban Lain

Bukan Karena Gula Sintaksis

Mengatakan bahwa NULL adalah nol karena gula sintaksis, tidak terlalu masuk akal, jika demikian mengapa tidak menggunakan indeks 0 dari sebuah array untuk menahan panjangnya?

Sebenarnya C adalah bahasa yang paling mirip dengan implementasi internal, apakah masuk akal untuk mengatakan bahwa C memilih nol hanya karena gula sintaksis? Mereka lebih suka memberikan kata kunci null (seperti yang dilakukan banyak bahasa lain) daripada memetakan nol ke NULL!

Karena saat ini mungkin hanya gula sintaksis, jelas bahwa maksud asli dari pengembang bahasa C bukanlah untuk gula sintaksis, seperti yang akan saya tunjukkan lebih lanjut.

1) Spesifikasi

Namun meskipun benar bahwa spesifikasi C berbicara dari konstanta 0 sebagai penunjuk nol (bagian 6.3.2.3), dan juga mendefinisikan NULL sebagai implementasi yang ditentukan (bagian 7.19 dalam spesifikasi C11, dan 7.17 dalam spesifikasi C99), Faktanya tetap bahwa dalam buku "The C Programming Language" yang ditulis oleh penemu C berikut ini dinyatakan dalam bagian 5.4:

C menjamin bahwa nol tidak pernah menjadi alamat yang valid untuk data, sehingga nilai kembali nol dapat digunakan untuk menandakan peristiwa abnormal, dalam hal ini, tidak ada spasi.

Pointer dan integer tidak dapat dipertukarkan, Nol adalah satu-satunya pengecualian: nol konstan dapat diberikan ke pointer, dan pointer dapat dibandingkan dengan nol konstan. Konstanta simbolik NULL sering digunakan di tempat nol, sebagai mnemonik untuk menunjukkan dengan lebih jelas bahwa ini adalah nilai khusus untuk sebuah pointer. NULL didefinisikan dalam. Kami akan menggunakan NULL untuk selanjutnya.

Seperti yang dapat dilihat (dari kata-kata "alamat nol") setidaknya maksud asli dari penulis C adalah alamat nol, dan bukan nol konstan, selain itu tampak dari kutipan ini bahwa alasan mengapa spesifikasi berbicara dari konstanta nol mungkin tidak mengecualikan ekspresi yang mengevaluasi ke nol, tetapi menyertakan konstanta bilangan bulat nol menjadi satu-satunya konstanta bilangan bulat yang diizinkan untuk digunakan dalam konteks penunjuk tanpa casting.

2) Ringkasan

Meskipun spesifikasi tidak mengatakan secara eksplisit bahwa alamat nol dapat diperlakukan berbeda dari konstanta nol, ia tidak mengatakan bahwa tidak, dan faktanya ketika berurusan dengan konstanta penunjuk-nol itu tidak mengklaimnya sebagai implementasi yang didefinisikan seperti itu dilakukan oleh konstanta yang ditentukan NULL , alih-alih mengklaimnya sebagai nol, menunjukkan bahwa mungkin ada perbedaan antara konstanta nol dan alamat nol.

(Namun jika ini kasusnya saya hanya bertanya-tanya mengapa NULL adalah implementasi yang ditentukan, karena dalam kasus seperti itu NULL juga bisa menjadi nol konstan, karena kompiler tetap harus mengubah semua konstanta nol menjadi implementasi aktual yang ditentukan NULL?)

Namun saya tidak melihat ini dalam tindakan nyata, dan dalam platform umum alamat nol dan nol konstan diperlakukan sama, dan menampilkan pesan kesalahan yang sama.

Selain itu, faktanya adalah bahwa sistem operasi saat ini sebenarnya menyimpan seluruh halaman pertama (kisaran 0x0000 hingga 0xFFFF), hanya untuk mencegah akses ke alamat nol karena penunjuk C's NULL, (lihat http://en.wikipedia.org/wiki/ Zero_page , serta "Windows Via C / C ++ oleh Jeffrey Richter dan Christophe Nasarre (diterbitkan oleh Microsoft Press)").

Jadi saya akan meminta dari siapa pun yang mengaku benar-benar melihatnya beraksi, untuk menentukan platform, dan kompiler, dan kode persis yang sebenarnya dia lakukan, (meskipun karena definisi yang tidak jelas dalam spesifikasi [seperti yang telah saya tunjukkan] kompiler apa pun dan platform bebas untuk melakukan apapun yang dia inginkan).

Namun tampaknya penulis C tidak memikirkan hal ini, dan mereka berbicara tentang "alamat nol", dan bahwa "C menjamin bahwa itu tidak pernah menjadi alamat yang valid", serta "NULL hanyalah a mnemonic ", dengan jelas menunjukkan bahwa niat aslinya bukanlah untuk" gula sintaksis ".

Bukan Karena Sistem Operasi

Juga mengklaim bahwa sistem operasi menolak akses ke alamat nol, karena beberapa alasan:

1) Ketika C ditulis tidak ada batasan seperti itu, seperti yang bisa dilihat di wikipage ini http://en.wikipedia.org/wiki/Zero_page .

2) Faktanya adalah bahwa kompiler C tidak mengakses alamat memori nol.

Ini tampaknya fakta dari makalah berikut oleh BellLabs ( http://www.cs.bell-labs.com/who/dmr/primevalC.html )

Kedua penyusun berbeda dalam detail cara mereka mengatasinya. Di awal, awal ditemukan dengan menamai fungsi; di kemudian hari, awal hanya dianggap 0. Ini menunjukkan bahwa kompilator pertama ditulis sebelum kita memiliki mesin dengan pemetaan memori, jadi program asal tidak berada di lokasi 0, sedangkan pada saat yang kedua, kami memiliki PDP-11 yang menyediakan pemetaan.

(Faktanya pada hari ini (seperti yang saya kutip referensi di atas dari wikipedia dan microsoft press), alasan untuk membatasi akses ke alamat nol adalah karena petunjuk C's NULL! Jadi pada akhirnya ternyata sebaliknya!)

3) Ingatlah bahwa C juga digunakan untuk menulis sistem operasi, dan bahkan C compiler!

Faktanya C dikembangkan untuk tujuan menulis sistem operasi UNIX dengannya, dan karena itu tampaknya tidak ada alasan mengapa mereka harus membatasi diri dari alamat nol.

(Hardware) Penjelasan Tentang Bagaimana Komputer (Secara Fisik) Mampu Mengakses Alamat Nol

Ada hal lain yang ingin saya jelaskan di sini, bagaimana mungkin merujuk alamat nol sama sekali?

Pikirkan sejenak, alamat diambil oleh prosesor, dan kemudian dikirim sebagai tegangan pada bus memori, yang kemudian digunakan oleh sistem memori untuk sampai ke alamat sebenarnya, namun alamat nol berarti tidak ada tegangan. , jadi bagaimana perangkat keras fisik dari sistem memori mengakses alamat nol?

Jawabannya adalah, bahwa alamat nol adalah default, dan dengan kata lain alamat nol selalu dapat diakses oleh sistem memori ketika bus memori benar-benar mati, dan dengan demikian setiap permintaan untuk membaca atau menulis tanpa menentukan alamat sebenarnya (yang mana adalah kasus dengan alamat nol) secara otomatis mengakses alamat nol.

yo hal
sumber
1
Saya tidak meremehkan Anda, tetapi posting Anda memiliki beberapa ketidakakuratan faktual, misalnya. bahwa memori fisik pada offset 0 tidak mungkin diakses (karena semua sakelar mati? benar-benar?), 0 dan konstanta 0 dapat dipertukarkan (mungkin tidak) dan lain-lain.
Hasturkun
Mengenai 0 dan konstanta nol, inilah yang dikatakan buku asli, dan ini yang ditunjukkan oleh pengujian aktual, apakah Anda menemukan perbedaan nyata antara keduanya? Jika ya, kompiler dan platform mana? Sementara banyak jawaban menunjukkan bahwa ada perbedaan yang belum saya temukan, dan mereka tidak memiliki referensi untuk menunjukkan perbedaan. Bahkan menurut en.wikipedia.org/wiki/Zero_page Dan juga "Windows Via C / C ++ oleh Jeffrey Richter dan Christophe Nasarre (diterbitkan oleh Microsoft Press)" di seluruh halaman pertama! dilindungi di komputer modern hanya untuk mencegah null (sebenarnya membuang lebih dari satu byte!)
yoel halb
Tentu saja pola bit alamat digunakan untuk memilih apa yang sedang dibaca. Biasanya memang demikian. bagaimanapun, saya tidak ingin berdebat dengan Anda, saya hanya menunjukkan mengapa Anda mungkin mendapat suara negatif.
Hasturkun
Saya tidak setuju dengan klaim Anda. Saya juga tidak tertarik untuk melanjutkan diskusi ini.
Hasturkun
6
Klaim perangkat keras tidak masuk akal. Untuk membaca address zero, drive! Chip Select low,! RAS high,! CAS low,! WE high, and all address lines low. Saat bus mati,! CS tinggi.
MSalters