Apakah potongan kode ini valid (dan perilaku yang ditentukan)?
int &nullReference = *(int*)0;
Kedua g ++ dan dentang ++ kompilasi tanpa peringatan apapun, bahkan ketika menggunakan -Wall
, -Wextra
, -std=c++98
, -pedantic
, -Weffc++
...
Tentu saja referensi sebenarnya tidak null, karena tidak dapat diakses (itu berarti mendereferensi pointer nol), tetapi kita dapat memeriksa apakah itu null atau tidak dengan memeriksa alamatnya:
if( & nullReference == 0 ) // null reference
c++
reference
null
language-lawyer
peoro
sumber
sumber
if (false)
, menghilangkan centang, justru karena referensi tidak boleh kosong. Versi terdokumentasi yang lebih baik ada di kernel Linux, di mana pemeriksaan NULL yang sangat mirip telah dioptimalkan: isc.sans.edu/diary.html?storyid=6820Jawaban:
Referensi bukanlah petunjuk.
8.3.2 / 1:
1,9 / 4:
Seperti yang Yohanes katakan dalam jawaban yang dihapus, ada beberapa keraguan apakah "mendereferensi penunjuk nol" harus secara kategoris dinyatakan sebagai perilaku yang tidak terdefinisi. Tapi ini bukan salah satu kasus yang menimbulkan keraguan, karena penunjuk nol jelas tidak menunjuk ke "objek atau fungsi yang valid", dan tidak ada keinginan dalam komite standar untuk memperkenalkan referensi nol.
sumber
&*p
secarap
universal, hal itu tidak mengesampingkan perilaku tidak terdefinisi (yang menurut sifatnya mungkin "tampak berhasil"); dan saya tidak setuju bahwatypeid
ekspresi yang berusaha untuk menentukan jenis "pointer nol dereferensi" sebenarnya dereferensi penunjuk nol. Saya telah melihat orang-orang berdebat dengan serius yang&a[size_of_array]
tidak dapat dan tidak boleh diandalkan, dan bagaimanapun lebih mudah dan aman untuk hanya menulisa + size_of_array
.*p
, kapanp
pointer nol. C ++ saat ini tidak memiliki gagasan tentang nilai l kosong, yang ingin diperkenalkan oleh edisi 232.typeid
karya berdasarkan sintaks, bukan berdasarkan semantik. Artinya, jika Anda melakukannyatypeid(0, *(ostream*)0)
Anda lakukan memiliki perilaku tidak terdefinisi - tidak adabad_typeid
dijamin untuk dilempar, meskipun Anda lulus lvalue yang dihasilkan dari pointer dereference nol semantik. Tapi secara sintaksis di tingkat atas, ini bukan dereferensi, melainkan ekspresi operator koma.Jawabannya tergantung pada sudut pandang Anda:
Jika Anda menilai dengan standar C ++, Anda tidak bisa mendapatkan referensi null karena Anda mendapatkan perilaku tidak terdefinisi terlebih dahulu. Setelah kejadian pertama dari perilaku tidak terdefinisi, standar memungkinkan segala sesuatu terjadi. Jadi, jika Anda menulis
*(int*)0
, Anda sudah memiliki perilaku yang tidak terdefinisi seperti Anda, dari sudut pandang standar bahasa, mengabaikan pointer nol. Sisa program tidak relevan, setelah ungkapan ini dijalankan, Anda keluar dari permainan.Namun, dalam praktiknya, referensi null dapat dengan mudah dibuat dari pointer null, dan Anda tidak akan menyadarinya sampai Anda benar-benar mencoba mengakses nilai di belakang referensi null. Contoh Anda mungkin agak terlalu sederhana, karena setiap kompiler pengoptimalan yang baik akan melihat perilaku tidak terdefinisi, dan hanya mengoptimalkan apa pun yang bergantung padanya (referensi nol bahkan tidak akan dibuat, itu akan dioptimalkan).
Namun, pengoptimalan tersebut bergantung pada compiler untuk membuktikan perilaku tidak terdefinisi, yang mungkin tidak dapat dilakukan. Pertimbangkan fungsi sederhana ini di dalam file
converter.cpp
:Ketika kompilator melihat fungsi ini, ia tidak tahu apakah pointernya adalah pointer null atau bukan. Jadi itu hanya menghasilkan kode yang mengubah pointer apa pun menjadi referensi yang sesuai. (Btw: Ini adalah noop karena pointer dan referensi adalah binatang yang sama persis di assembler.) Sekarang, jika Anda memiliki file lain
user.cpp
dengan kodekompilator tidak tahu bahwa
toReference()
akan mendereferensi penunjuk yang diteruskan, dan menganggap bahwa ia mengembalikan referensi yang valid, yang dalam praktiknya akan menjadi referensi nol. Panggilan berhasil, tetapi ketika Anda mencoba menggunakan referensi, program lumpuh. Semoga. Standar tersebut memungkinkan terjadinya apa saja, termasuk penampilan gajah merah muda.Anda mungkin bertanya mengapa ini relevan, lagipula, perilaku tidak terdefinisi sudah dipicu di dalam
toReference()
. Jawabannya adalah debugging: Referensi nol dapat menyebar dan berkembang biak seperti yang dilakukan pointer nol. Jika Anda tidak menyadari bahwa referensi null bisa ada, dan belajar untuk menghindari pembuatannya, Anda mungkin meluangkan cukup waktu untuk mencoba mencari tahu mengapa fungsi anggota Anda tampaknya macet ketika hanya mencoba membaca anggota lama yang biasaint
(jawaban: contoh dalam panggilan anggota itu referensi nol, begituthis
juga pointer nol, dan anggota Anda dihitung untuk ditempatkan sebagai alamat 8).Jadi bagaimana dengan memeriksa referensi null? Anda memberi garis
dalam pertanyaan Anda. Nah, itu tidak akan berhasil: Menurut standar, Anda memiliki perilaku yang tidak terdefinisi jika Anda mendereferensi penunjuk nol, dan Anda tidak dapat membuat referensi nol tanpa mendereferensi penunjuk null, jadi referensi null hanya ada di dalam ranah perilaku tidak terdefinisi. Karena kompilator Anda mungkin berasumsi bahwa Anda tidak memicu perilaku yang tidak ditentukan, ia dapat berasumsi bahwa tidak ada yang namanya referensi null (meskipun ia dengan mudah akan mengeluarkan kode yang menghasilkan referensi null!). Dengan demikian, ia melihat
if()
kondisinya, menyimpulkan bahwa itu tidak mungkin benar, dan hanya membuang seluruhif()
pernyataan. Dengan diperkenalkannya pengoptimalan waktu tautan, menjadi sangat tidak mungkin untuk memeriksa referensi null dengan cara yang kuat.TL; DR:
Referensi kosong agaknya merupakan keberadaan yang mengerikan:
Keberadaan mereka tampaknya tidak mungkin (= menurut standar),
tetapi mereka ada (= oleh kode mesin yang dihasilkan),
tetapi Anda tidak dapat melihatnya jika ada (= upaya Anda akan dioptimalkan),
tetapi mereka mungkin membunuh Anda tanpa menyadarinya (= program Anda macet di titik-titik aneh, atau lebih buruk lagi).
Satu-satunya harapan Anda adalah mereka tidak ada (= tulis program Anda untuk tidak membuatnya).
Saya berharap hal itu tidak akan menghantui Anda!
sumber
Jika niat Anda adalah menemukan cara untuk merepresentasikan null dalam enumerasi objek tunggal, maka sebaiknya gunakan (de) referensi null (it C ++ 11, nullptr) sebagai ide yang buruk.
Mengapa tidak mendeklarasikan objek tunggal statis yang mewakili NULL dalam kelas sebagai berikut dan menambahkan operator cast-to-pointer yang mengembalikan nullptr?
Sunting: Memperbaiki beberapa kesalahan ketik dan menambahkan pernyataan-if di main () untuk menguji operator cast-to-pointer benar-benar berfungsi (yang saya lupa .. saya buruk) - 10 Maret 2015 -
sumber
clang ++ 3.5 bahkan memperingatkannya:
sumber