Apa tipe data uintptr_t

Jawaban:

199

uintptr_tadalah tipe integer yang tidak ditandatangani yang mampu menyimpan data pointer. Yang biasanya berarti ukurannya sama dengan pointer.

Secara opsional didefinisikan dalam C ++ 11 dan standar selanjutnya.

Alasan umum untuk menginginkan tipe integer yang dapat menampung tipe pointer arsitektur adalah untuk melakukan operasi spesifik integer pada pointer, atau untuk mengaburkan tipe pointer dengan menyediakannya sebagai "handle" integer.

Sunting: Perhatikan bahwa Steve Jessop memiliki beberapa detail tambahan yang sangat menarik (yang tidak akan saya curi) di jawaban lain di sini untuk Anda yang bertipe jagoan :)

Drew Dormann
sumber
53
Perhatikan bahwa size_thanya perlu cukup untuk menampung ukuran objek terbesar, dan bisa lebih kecil dari pointer. Ini diharapkan pada arsitektur tersegmentasi seperti 8086 (16 bit size_t, tapi 32 bit void*)
MSalters
3
untuk mewakili perbedaan antara dua petunjuk, Anda harus ptrdiff_t. uintptr_ttidak dimaksudkan untuk itu.
jalf
3
@jalf: Untuk perbedaan, ya, tapi untuk jarak , Anda ingin tipe yang tidak ditandatangani.
Drew Dormann
Definisi uintptr_t tampaknya tidak wajib (jadi bukan standar) bahkan di C ++ 11! cplusplus.com/reference/cstdint (Saya mendapat petunjuk dari jawaban Steve Jessop)
Antonio
2
@MarcusJ unsigned intbiasanya tidak cukup besar. Tapi itu mungkin cukup besar. Jenis ini ada khusus untuk menghapus semua "asumsi" .
Drew Dormann
239

Hal pertama, pada saat pertanyaan itu diajukan, uintptr_ttidak ada di C ++. Ada dalam C99, in <stdint.h>, sebagai tipe opsional. Banyak kompiler C ++ 03 menyediakan file itu. Itu juga di C ++ 11, di, di <cstdint>mana lagi itu opsional, dan yang merujuk ke C99 untuk definisi.

Di C99, ini didefinisikan sebagai "tipe integer yang tidak ditandai dengan properti bahwa setiap pointer yang valid ke void dapat dikonversi ke tipe ini, kemudian dikonversi kembali ke pointer ke void, dan hasilnya akan dibandingkan dengan pointer asli".

Anggap ini berarti apa yang dikatakannya. Itu tidak mengatakan apa-apa tentang ukuran.

uintptr_tmungkin ukurannya sama dengan a void*. Mungkin lebih besar. Bisa dibayangkan bisa lebih kecil, meskipun pendekatan implementasi C ++ buruk. Misalnya pada beberapa platform hipotetis di mana void*32 bit, tetapi hanya 24 bit ruang alamat virtual yang digunakan, Anda bisa memiliki 24-bit uintptr_tyang memenuhi persyaratan. Saya tidak tahu mengapa implementasi akan melakukan itu, tetapi standar mengizinkannya.

Steve Jessop
sumber
8
Terima kasih untuk "<stdint.h>". Aplikasi saya tidak dikompilasi karena deklarasi uintptr_t. Tetapi ketika saya membaca komentar Anda, saya menambahkan "#include <stdint.h>" dan ya sekarang berfungsi. Terima kasih!
JavaRunner
2
Untuk mengalokasikan memori yang disejajarkan , di antara kegunaan lain, misalnya?
legends2k
4
Kasus penggunaan umum lainnya dari casting pointer ke int adalah untuk membuat pegangan buram yang menyembunyikan pointer. Ini berguna untuk mengembalikan referensi ke suatu objek dari API di mana Anda ingin merahasiakan objek ke perpustakaan dan mencegah aplikasi dari memiliki akses ke sana. Aplikasi ini kemudian dipaksa untuk menggunakan API untuk melakukan operasi pada objek
Joel Cunningham
3
@ JoelCunningham: itu berfungsi tetapi tidak terlalu berbeda dengan menggunakan void*. Ini memengaruhi kemungkinan arah di masa mendatang, terutama, jika Anda mungkin ingin mengubah untuk menggunakan sesuatu yang benar-benar hanya merupakan pegangan bilangan bulat, bukan penunjuk yang dikonversi sama sekali.
Steve Jessop
2
@CiroSantilli 烏坎 事件 2016 六四 事件 法轮功 Kasus penggunaan umum adalah hanya menyampaikan int ke API yang mengharapkan void * untuk data umum. Menghemat penekanan tombol typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;. Sebaliknya: callback.dataPtr = (void*)val. Di sisi lain, Anda tentu saja void*harus dan harus mengembalikannya int.
Francesco Dondi
19

Ini adalah tipe integer yang tidak ditandai persis ukuran pointer. Setiap kali Anda perlu melakukan sesuatu yang tidak biasa dengan pointer - seperti misalnya membalikkan semua bit (jangan tanya mengapa) Anda melemparkannya ke uintptr_tdan memanipulasinya sebagai bilangan bulat biasa, lalu balas kembali.

sharptooth
sumber
6
Tentu saja Anda bisa melakukan itu, tetapi itu tentu saja perilaku yang tidak terdefinisi. Saya percaya maka satu-satunya hal yang dapat Anda lakukan dengan hasil pemeran untuk uintptr_t adalah meneruskannya dengan tidak berubah dan melemparkannya kembali - yang lainnya adalah UB.
sleske
Ada saat-saat di mana Anda perlu bermain dengan bit, dan itu biasanya akan menghasilkan kesalahan kompiler. Contoh umum adalah menegakkan memori selaras 16-byte untuk video tertentu dan aplikasi penting kinerja. stackoverflow.com/questions/227897/…
Cloud
3
@sleske itu tidak benar. Pada mesin yang memiliki tipe self-aligned, dua bit paling tidak signifikan dari pointer akan menjadi nol (karena alamat adalah kelipatan dari 4 atau 8). Saya telah melihat program yang mengeksploitasi ini untuk mengompresi data ..
saadtaame
3
@saadtaame: Saya hanya menunjukkan bahwa ini adalah UB sesuai dengan standar C . Itu tidak berarti itu tidak dapat didefinisikan pada beberapa sistem - kompiler dan runtime bebas untuk mendefinisikan perilaku spesifik untuk sesuatu yang UB dalam standar C. Jadi tidak ada kontradiksi :-).
sleske
2
Ini belum tentu ukuran pointer. Semua jaminan standar adalah bahwa mengkonversi void*nilai pointer ke uintptr_tdan kembali lagi menghasilkan void*nilai yang sebanding dengan pointer asli. uintptr_tbiasanya ukurannya sama dengan void*, tetapi itu tidak dijamin, juga tidak ada jaminan bahwa bit dari nilai yang dikonversi memiliki arti tertentu. Dan tidak ada jaminan bahwa itu dapat menyimpan nilai pointer-ke-fungsi yang dikonversi tanpa kehilangan informasi. Akhirnya, itu tidak dijamin ada.
Keith Thompson
15

Sudah ada banyak jawaban bagus untuk bagian "apa itu tipe data uintptr_t". Saya akan mencoba menjawab "untuk apa itu digunakan?" bagian dalam posting ini.

Terutama untuk operasi bitwise pada pointer. Ingat bahwa dalam C ++ seseorang tidak dapat melakukan operasi bitwise pada pointer. Untuk alasan, lihat Mengapa Anda tidak bisa melakukan operasi bitwise pada pointer di C, dan apakah ada cara untuk mengatasi ini?

Jadi, untuk melakukan operasi bitwise pada pointer, seseorang harus melemparkan pointer untuk mengetikkan unitpr_t dan kemudian melakukan operasi bitwise.

Berikut adalah contoh dari fungsi yang baru saja saya tulis untuk melakukan bitwise eksklusif atau dari 2 pointer untuk disimpan dalam daftar tertaut XOR sehingga kita dapat melintasi kedua arah seperti daftar yang tertaut ganda tetapi tanpa penalti menyimpan 2 pointer di setiap node .

 template <typename T>
 T* xor_ptrs(T* t1, T* t2)
 {
     return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
  }
BGR
sumber
1
selain bit aritmatika juga bagus jika Anda ingin memiliki semantik berdasarkan alamat, bukan jumlah objek.
Alex
4

Menjalankan risiko mendapatkan lencana Necromancer lain, saya ingin menambahkan satu penggunaan yang sangat baik untuk uintptr_t (atau bahkan intptr_t) dan itu adalah penulisan kode tertanam yang dapat diuji. Saya menulis sebagian besar kode tertanam yang ditargetkan pada berbagai lengan dan prosesor saat ini tensilica. Ini memiliki berbagai lebar bus asli dan tensilica sebenarnya adalah arsitektur Harvard dengan kode dan data bus terpisah yang dapat berbeda lebar. Saya menggunakan gaya pengembangan yang digerakkan oleh tes untuk sebagian besar kode saya yang berarti saya melakukan tes unit untuk semua unit kode yang saya tulis. Pengujian unit pada perangkat keras target yang sebenarnya adalah sebuah kerumitan jadi saya biasanya menulis semuanya pada PC berbasis Intel baik di Windows atau Linux menggunakan Ceedling dan GCC. Yang sedang berkata, banyak kode tertanam melibatkan sedikit twiddling dan manipulasi alamat. Sebagian besar mesin Intel saya 64 bit. Jadi jika Anda akan menguji kode manipulasi alamat Anda memerlukan objek umum untuk melakukan matematika. Jadi, uintptr_t memberi Anda cara mandiri untuk debugging kode Anda sebelum Anda mencoba menggunakan perangkat keras target. Masalah lain adalah untuk beberapa mesin atau bahkan model memori pada beberapa kompiler, fungsi pointer dan pointer data memiliki lebar yang berbeda. Pada mesin-mesin itu, kompiler bahkan mungkin tidak mengizinkan casting di antara dua kelas, tetapi uintptr_t harus bisa menahan keduanya.

bd2357
sumber
Tidak yakin jika relevan ... Apakah Anda mencoba "typedefs buram"? Vide: youtube.com/watch?v=jLdSjh8oqmE
shycha
@sleske Saya berharap itu tersedia dalam C. Tetapi memiliki stdint.h lebih baik daripada tidak sama sekali. (Juga berharap enum di mana diketik lebih kuat tetapi kebanyakan debugger melakukan pekerjaan yang baik untuk mencari tahu mereka dengan cara apa pun)
bd2357