AFAIK, meskipun kita tidak dapat membuat array memori statis berukuran 0, tetapi kita dapat melakukannya dengan yang dinamis:
int a[0]{}; // Compile-time error
int* p = new int[0]; // Is well-defined
Seperti yang sudah saya baca, p
bertindak seperti elemen satu-melewati-akhir. Saya dapat mencetak alamat yang p
menunjuk.
if(p)
cout << p << endl;
Meskipun saya yakin kita tidak bisa melakukan dereferensi pointer itu (elemen terakhir-terakhir) karena kita tidak bisa dengan iterator (elemen terakhir-terakhir), tetapi apa yang saya tidak yakin adalah apakah menambah pointer itu
p
? Apakah perilaku tidak terdefinisi (UB) seperti dengan iterator?p++; // UB?
c++
pointers
undefined-behavior
dynamic-arrays
Itachi Uchiwa
sumber
sumber
std::vector
dengan item dengan 0 di dalamnya.begin()
sudah sama denganend()
sehingga Anda tidak bisa menambah iterator yang menunjuk di awal.Jawaban:
Pointer ke elemen array diperbolehkan untuk menunjuk ke elemen yang valid, atau yang melewati akhir. Jika Anda menambah pointer dengan cara yang berjalan lebih dari satu melewati akhir, perilaku tidak terdefinisi.
Untuk array ukuran 0 Anda,
p
sudah menunjuk satu melewati akhir, jadi penambahan itu tidak diperbolehkan.Lihat C ++ 17 8.7 / 4 tentang
+
operator (++
memiliki batasan yang sama):sumber
x[i]
adalah sama sepertix[i + j]
ketika keduanyai
danj
memiliki nilai 0?x[i]
adalah elemen yang sama sepertix[i+j]
jikaj==0
.Saya kira Anda sudah memiliki jawabannya; Jika Anda melihat sedikit lebih dalam: Anda telah mengatakan bahwa menambahkan iterator off-the-end adalah UB dengan demikian: Jawaban ini ada di dalam apa itu iterator?
Iterator hanyalah sebuah objek yang memiliki pointer dan menambahkan bahwa iterator benar-benar menambah pointer yang dimilikinya. Jadi, dalam banyak aspek, iterator ditangani dengan menggunakan pointer.
Ini dari C ++ primer 5 edisi oleh Lipmann.
Jadi UB tidak melakukannya.
sumber
Dalam arti yang paling ketat, ini bukan Perilaku Tidak Terdefinisi, tetapi implementasi-didefinisikan. Jadi, meskipun tidak disarankan jika Anda berencana untuk mendukung arsitektur non-mainstream, Anda mungkin dapat melakukannya.
Kutipan standar yang diberikan oleh interjay adalah yang baik, menunjukkan UB, tetapi itu hanya hit terbaik kedua menurut saya, karena berurusan dengan pointer-pointer aritmatika (lucu, satu secara eksplisit UB, sedangkan yang lain tidak). Ada paragraf yang membahas operasi dalam pertanyaan secara langsung:
Oh, tunggu sebentar, tipe objek yang didefinisikan sepenuhnya? Itu saja? Maksudku, sungguh, ketik ? Jadi, Anda tidak perlu objek sama sekali?
Dibutuhkan sedikit bacaan untuk benar-benar menemukan petunjuk bahwa sesuatu di sana mungkin tidak begitu jelas. Karena sejauh ini, sepertinya Anda benar-benar diizinkan melakukannya, tidak ada batasan.
[basic.compound] 3
membuat pernyataan tentang apa jenis penunjuk yang mungkin dimiliki, dan karena tidak satu pun dari tiga penunjuk lainnya, hasil operasi Anda akan jelas berada di bawah 3.4: penunjuk tidak valid .Namun itu tidak mengatakan bahwa Anda tidak diizinkan memiliki pointer yang tidak valid. Sebaliknya, ia mencantumkan beberapa kondisi normal yang sangat umum (misalnya, akhir durasi penyimpanan) di mana pointer secara teratur menjadi tidak valid. Jadi itu tampaknya hal yang diijinkan terjadi. Dan memang:
Kami melakukan "yang lain" di sana, jadi itu bukan Perilaku Tidak Terdefinisi, tetapi ditentukan oleh implementasi, dengan demikian umumnya diperbolehkan (kecuali jika implementasi secara eksplisit mengatakan sesuatu yang berbeda).
Sayangnya, itu bukan akhir dari cerita. Meskipun hasil bersih tidak berubah lagi dari sini, itu semakin membingungkan, semakin lama Anda mencari "pointer":
Baca sebagai: Oke, siapa peduli! Selama pointer menunjuk suatu tempat di memori , saya baik-baik saja?
Baca sebagai: OK, diturunkan dengan aman, apa pun. Itu tidak menjelaskan apa ini, juga tidak mengatakan saya benar-benar membutuhkannya. Yang diturunkan dengan aman. Rupanya saya masih bisa memiliki pointer yang tidak aman dengan baik. Saya menduga bahwa melakukan dereferensi pada mereka mungkin bukan ide yang bagus, tetapi sangat memungkinkan untuk memilikinya. Itu tidak mengatakan sebaliknya.
Oh, jadi mungkin tidak masalah, hanya apa yang saya pikirkan. Tapi tunggu ... "mungkin tidak"? Itu artinya, mungkin juga . Bagaimana aku tahu?
Tunggu, jadi mungkin saja saya perlu memanggil
declare_reachable()
setiap pointer? Bagaimana aku tahu?Sekarang, Anda dapat mengonversi ke
intptr_t
, yang terdefinisi dengan baik, memberikan representasi integer dari pointer yang diturunkan dengan aman. Untuk yang, tentu saja, sebagai bilangan bulat, itu sah dan terdefinisi dengan baik untuk menambahkannya sesuka Anda.Dan ya, Anda dapat mengonversi
intptr_t
kembali ke sebuah pointer, yang juga terdefinisi dengan baik. Hanya saja, tidak menjadi nilai asli, itu tidak lagi dijamin bahwa Anda memiliki pointer yang diturunkan dengan aman (jelas). Namun, secara keseluruhan, untuk surat standar, sementara sedang didefinisikan implementasi, ini adalah hal yang 100% sah untuk dilakukan:Tangkapan
Pointer hanyalah bilangan bulat biasa, hanya Anda yang menggunakannya sebagai pointer. Oh andai saja itu benar!
Sayangnya, ada arsitektur di mana itu tidak benar sama sekali, dan hanya menghasilkan pointer yang tidak valid (tidak men-dereferensinya, hanya dengan memasukkannya ke register pointer) akan menyebabkan jebakan.
Jadi itulah dasar "implementasi didefinisikan". Itu, dan fakta bahwa incrementing pointer setiap kali Anda inginkan, tolong bisa saja menyebabkan overflow, yang standar tidak ingin berurusan dengan. Akhir ruang alamat aplikasi mungkin tidak sesuai dengan lokasi overflow, dan Anda bahkan tidak tahu apakah ada yang namanya overflow untuk pointer pada arsitektur tertentu. Semua dalam semua itu berantakan mimpi buruk tidak dalam kaitannya dengan manfaat yang mungkin.
Berhubungan dengan kondisi objek satu-lampau di sisi lain, mudah: Implementasinya harus memastikan tidak ada objek yang pernah dialokasikan sehingga byte terakhir di ruang alamat ditempati. Jadi itu didefinisikan dengan baik karena berguna dan mudah untuk dijamin.
sumber
+
operator (dari mana++
mengalir) yang berarti bahwa menunjuk setelah "satu-setelah-akhir" tidak didefinisikan.