Pada implementasi dengan model memori datar (pada dasarnya semuanya), casting untuk uintptr_t
Just Work.
(Tetapi lihat apakah perbandingan pointer ditandatangani atau tidak ditandatangani dalam 64-bit x86? Untuk diskusi apakah Anda harus memperlakukan pointer sebagai ditandatangani atau tidak, termasuk masalah pembentukan pointer di luar objek yang merupakan UB dalam C.)
Tapi sistem dengan model memori non-datar lakukan ada, dan berpikir tentang mereka dapat membantu menjelaskan situasi saat ini, seperti C ++ memiliki spesifikasi yang berbeda untuk <
vs std::less
.
Bagian dari titik <
pada pointer ke objek yang terpisah menjadi UB di C (atau setidaknya tidak ditentukan dalam beberapa revisi C ++) adalah untuk memungkinkan mesin aneh, termasuk model memori non-flat.
Contoh terkenal adalah mode real x86-16 di mana pointer adalah segmen: offset, membentuk alamat linear 20-bit via (segment << 4) + offset
. Alamat linear yang sama dapat diwakili oleh beberapa kombinasi seg: off yang berbeda.
C ++ std::less
pada pointer pada ISA aneh mungkin perlu mahal , misalnya "menormalkan" segmen: offset pada x86-16 untuk memiliki offset <= 15. Namun, tidak ada cara portabel untuk mengimplementasikan ini. Manipulasi yang diperlukan untuk menormalkan suatu uintptr_t
(atau objek-representasi dari objek pointer) adalah implementasi khusus.
Tetapi bahkan pada sistem di mana C ++ std::less
harus mahal, <
tidak harus. Misalnya, dengan mengasumsikan model memori "besar" di mana objek cocok dalam satu segmen, <
cukup bandingkan bagian offset dan bahkan tidak repot dengan bagian segmen. (Pointer di dalam objek yang sama akan memiliki segmen yang sama, dan sebaliknya itu UB dalam C. C ++ 17 diubah menjadi hanya "tidak ditentukan", yang mungkin masih memungkinkan melompati normalisasi dan hanya membandingkan offset.) Ini mengasumsikan semua pointer ke bagian mana pun suatu benda selalu menggunakan nilai yang sama seg
, tidak pernah dinormalisasi. Ini yang Anda harapkan dari ABI untuk model memori "besar" dan bukan "besar". (Lihat diskusi dalam komentar ).
(Model memori semacam itu mungkin memiliki ukuran objek maksimal 64kiB misalnya, tetapi ruang alamat total maks jauh lebih besar yang memiliki ruang untuk banyak objek berukuran maksimal tersebut. ISO C memungkinkan implementasi memiliki batas ukuran objek yang lebih rendah dari nilai maks (tidak ditandai) size_t
dapat mewakili SIZE_MAX
,. Misalnya, bahkan pada sistem model memori datar, GNU C membatasi ukuran objek maks PTRDIFF_MAX
sehingga perhitungan ukuran dapat mengabaikan limpahan yang ditandatangani.) Lihat jawaban dan diskusi ini dalam komentar.
Jika Anda ingin mengizinkan objek yang lebih besar dari suatu segmen, Anda memerlukan model memori "besar" yang harus dikhawatirkan meluap bagian offset dari pointer ketika melakukan p++
perulangan melalui array, atau ketika melakukan aritmatika pengindeksan / penunjuk. Ini mengarah ke kode yang lebih lambat di mana-mana, tetapi mungkin berarti hal itu p < q
akan bekerja untuk pointer ke objek yang berbeda, karena implementasi yang menargetkan model memori "besar" biasanya akan memilih untuk menjaga semua pointer dinormalisasi sepanjang waktu. Lihat Apa yang dekat, jauh dan petunjuk besar? - beberapa kompiler C nyata untuk mode real x86 memang memiliki opsi untuk dikompilasi untuk model "besar" di mana semua pointer default ke "besar" kecuali dinyatakan sebaliknya.
Segmentasi x86 real-mode bukan satu-satunya model memori non-flat mungkin , itu hanya contoh konkret yang berguna untuk menggambarkan bagaimana itu ditangani oleh implementasi C / C ++. Dalam kehidupan nyata, implementasi diperpanjang ISO C dengan konsep far
vs near
pointer, yang memungkinkan programmer untuk memilih kapan mereka bisa pergi dengan hanya menyimpan / melewati bagian offset 16-bit, relatif terhadap beberapa segmen data umum.
Tetapi implementasi ISO C murni harus memilih antara model memori kecil (semuanya kecuali kode dalam 64kiB yang sama dengan pointer 16-bit) atau besar atau besar dengan semua pointer menjadi 32-bit. Beberapa loop dapat dioptimalkan dengan menambah hanya bagian offset, tetapi objek pointer tidak dapat dioptimalkan menjadi lebih kecil.
Jika Anda tahu apa manipulasi sihir untuk implementasi yang diberikan, Anda bisa menerapkannya dalam C murni . Masalahnya adalah bahwa sistem yang berbeda menggunakan pengalamatan yang berbeda dan detailnya tidak diparameterisasi oleh makro portabel apa pun.
Atau mungkin tidak: itu mungkin melibatkan melihat sesuatu dari tabel segmen khusus atau sesuatu, misalnya seperti mode terproteksi x86, bukan mode nyata di mana bagian segmen dari alamat adalah indeks, bukan nilai yang dibiarkan bergeser. Anda dapat mengatur segmen yang tumpang tindih sebagian dalam mode terproteksi, dan bagian pemilih segmen alamat bahkan tidak perlu dipesan dalam urutan yang sama dengan alamat basis segmen yang sesuai. Mendapatkan alamat linear dari pointer seg: off dalam mode terproteksi x86 mungkin melibatkan pemanggilan sistem, jika GDT dan / atau LDT tidak dipetakan ke halaman yang dapat dibaca dalam proses Anda.
(Tentu saja OS mainstream untuk x86 menggunakan model memori datar sehingga basis segmen selalu 0 (kecuali untuk penggunaan fs
atau gs
segmen penyimpanan thread-lokal ), dan hanya bagian "offset" 32-bit atau 64-bit yang digunakan sebagai penunjuk .)
Anda dapat secara manual menambahkan kode untuk berbagai platform tertentu, misalnya secara default menganggap datar, atau #ifdef
sesuatu untuk mendeteksi mode real x86 dan membaginya uintptr_t
menjadi dua bagian 16-bit untuk seg -= off>>4; off &= 0xf;
kemudian menggabungkan bagian-bagian itu kembali ke angka 32-bit.