Standar C menjamin itu size_t
adalah tipe yang dapat menampung indeks array apa pun. Ini berarti bahwa, secara logis, size_t
harus dapat menahan semua jenis pointer. Saya telah membaca di beberapa situs yang saya temukan di Google bahwa ini legal dan / atau harus selalu berfungsi:
void *v = malloc(10);
size_t s = (size_t) v;
Jadi di C99, standar memperkenalkan intptr_t
dan uintptr_t
jenis, yang ditandatangani dan jenis yang tidak ditandatangani dijamin untuk dapat menahan pointer:
uintptr_t p = (size_t) v;
Jadi apa perbedaan antara menggunakan size_t
dan uintptr_t
? Keduanya tidak ditandatangani, dan keduanya harus dapat menahan jenis pointer apa pun, sehingga keduanya tampak identik secara fungsional. Apakah ada alasan kuat yang nyata untuk menggunakan uintptr_t
(atau lebih baik, a void *
) daripada a size_t
, selain kejelasan? Dalam struktur buram, di mana lapangan hanya akan ditangani oleh fungsi internal, adakah alasan untuk tidak melakukan ini?
Dengan cara yang sama, ptrdiff_t
telah menjadi tipe bertanda yang mampu menahan perbedaan pointer, dan karenanya mampu menahan sebagian besar pointer, jadi bagaimana bedanya intptr_t
?
Bukankah semua tipe ini pada dasarnya melayani versi yang berbeda dari fungsi yang sama? Jika tidak, mengapa? Apa yang tidak bisa saya lakukan dengan salah satu dari mereka yang saya tidak bisa lakukan dengan yang lain? Jika demikian, mengapa C99 menambahkan dua jenis dasarnya tidak berguna untuk bahasa?
Saya bersedia untuk mengabaikan fungsi pointer, karena mereka tidak berlaku untuk masalah saat ini, tetapi jangan ragu untuk menyebutkannya, karena saya memiliki kecurigaan menyelinap mereka akan menjadi pusat jawaban "benar".
size_t
danuintptr_t
tetapi bagaimanaptrdiff_t
danintptr_t
- tidakkah keduanya dapat menyimpan rentang nilai yang sama di hampir semua platform? Mengapa memiliki tipe integer berukuran pointer dan unsigned, terutama jikaptrdiff_t
sudah melayani tujuan dari tipe integer ukuran pointer yang ditandatangani.size_t
setidaknya 16 bit, tetapiptrdiff_t
harus setidaknya 17 bit (yang dalam praktiknya berarti mungkin akan setidaknya 32 bit).SIZE_MAX
seharusnya tidak menjadi 2 ** 64. Ini menggunakan pengalamatan datar, ingatlah; tidak ada segmentasi yang diperlukan untuk memiliki ketidakcocokan antaraSIZE_MAX
dan rentang pointer data.Mengenai pernyataan Anda:
Ini sebenarnya sebuah kesalahan (kesalahpahaman yang dihasilkan dari alasan yang salah) (a) . Anda mungkin berpikir yang terakhir mengikuti dari yang pertama tetapi sebenarnya tidak demikian.
Pointer dan indeks array bukan hal yang sama. Sangat masuk akal untuk membayangkan implementasi yang sesuai yang membatasi array menjadi 65536 elemen tetapi memungkinkan pointer untuk menunjuk nilai apa pun ke dalam ruang alamat 128-bit yang besar.
C99 menyatakan bahwa batas atas suatu
size_t
variabel didefinisikan olehSIZE_MAX
dan ini bisa serendah 65535 (lihat C99 TR3, 7.18.3, tidak berubah dalam C11). Pointer akan sangat terbatas jika mereka terbatas pada kisaran ini dalam sistem modern.Dalam praktiknya, Anda mungkin akan menemukan bahwa asumsi Anda berlaku, tetapi itu bukan karena standar menjaminnya. Karena sebenarnya tidak menjamin itu.
(a) Ngomong-ngomong, ini bukan bentuk serangan pribadi, hanya menyatakan mengapa pernyataan Anda salah dalam konteks pemikiran kritis. Misalnya, alasan berikut juga tidak valid:
Kelucuan atau sebaliknya dari anak-anak anjing tidak ada hubungannya di sini, semua yang saya nyatakan adalah bahwa kedua fakta tidak mengarah pada kesimpulan, karena dua kalimat pertama memungkinkan adanya hal-hal lucu yang bukan anak - anak anjing.
Ini mirip dengan pernyataan pertama Anda yang belum tentu mengharuskan yang kedua.
sumber
ptrdiff_t
vs.intptr_t
).Saya akan membiarkan semua jawaban lain berdiri sendiri mengenai alasan dengan batasan segmen, arsitektur eksotis, dan sebagainya.
Bukankah perbedaan sederhana dalam alasan cukup untuk menggunakan jenis yang tepat untuk hal yang tepat?
Jika Anda menyimpan ukuran, gunakan
size_t
. Jika Anda menyimpan pointer, gunakanintptr_t
. Seseorang yang membaca kode Anda akan langsung tahu bahwa "aha, ini adalah ukuran sesuatu, mungkin dalam byte", dan "oh, inilah nilai pointer yang disimpan sebagai integer, untuk beberapa alasan".Kalau tidak, Anda bisa menggunakan
unsigned long
(atau, di zaman modern di siniunsigned long long
) , untuk semuanya. Ukuran bukanlah segalanya, ketikkan nama mengandung arti yang berguna karena membantu menjelaskan program.sumber
size_t
bidang.void*
,intptr_t
danuintptr_t
dijamin dapat mewakili pointer apa pun ke data.Mungkin saja ukuran array terbesar lebih kecil dari sebuah pointer. Pikirkan arsitektur tersegmentasi - pointer mungkin 32-bit, tetapi satu segmen mungkin hanya mampu mengatasi 64KB (misalnya arsitektur real-mode 8086).
Meskipun ini tidak umum digunakan dalam mesin desktop lagi, standar C dimaksudkan untuk mendukung bahkan arsitektur kecil khusus. Masih ada embedded system yang sedang dikembangkan dengan CPU 8 atau 16 bit misalnya.
sumber
size_t
bisa mengatasinya? Atau apakah array dinamis di beberapa segmen yang jauh masih terbatas pada pengindeksan dalam segmen mereka?str
fungsi dan Borland bahkan untukmem
fungsi (memset
,memcpy
,memmove
). Ini berarti Anda dapat menimpa sebagian memori ketika offset meluap, itu menyenangkan untuk debug pada platform tertanam kami.Saya akan membayangkan (dan ini berlaku untuk semua jenis nama) bahwa lebih baik menyampaikan niat Anda dalam kode.
Sebagai contoh, meskipun
unsigned short
danwchar_t
memiliki ukuran yang sama pada Windows (saya pikir), menggunakanwchar_t
bukannyaunsigned short
menunjukkan niat bahwa Anda akan menggunakannya untuk menyimpan karakter yang luas, daripada hanya beberapa nomor acak.sumber
wchar_t
jauh lebih besar daripadaunsigned short
menggunakan satu untuk yang lain akan keliru dan menciptakan masalah portabilitas yang serius (dan modern), sedangkan portabilitas menyangkutsize_t
danuintptr_t
tampaknya terletak di tanah yang jauh 1980-sesuatu (tusukan acak dalam kegelapan pada tanggal, di sana)size_t
danuintptr_t
masih tersirat menggunakan nama mereka.Melihat ke belakang dan ke depan, dan mengingat bahwa berbagai arsitektur aneh tersebar di lanskap, saya cukup yakin mereka mencoba untuk membungkus semua sistem yang ada dan juga menyediakan untuk semua sistem yang mungkin di masa depan.
Begitu yakin, cara penyelesaiannya, sejauh ini kami membutuhkan tidak banyak jenis.
Tetapi bahkan di LP64, sebuah paradigma yang agak umum, kami membutuhkan size_t dan ssize_t untuk antarmuka panggilan sistem. Orang bisa membayangkan warisan yang lebih terbatas atau sistem masa depan, di mana menggunakan tipe 64-bit penuh itu mahal dan mereka mungkin ingin menyodok pada I / O ops lebih besar dari 4GB tetapi masih memiliki pointer 64-bit.
Saya pikir Anda harus bertanya-tanya: apa yang mungkin telah dikembangkan, apa yang akan terjadi di masa depan. (Mungkin 128-bit pointer sistem terdistribusi internet-lebar, tetapi tidak lebih dari 64 bit dalam panggilan sistem, atau bahkan mungkin batas 32-bit "warisan". :-) Gambar yang sistem warisan mungkin mendapatkan kompiler C baru .. .
Juga, lihat apa yang ada di sekitar itu. Selain model memori real-mode zillion 286, bagaimana dengan mainframe pointer 60-bit word / 18-bit CDC? Bagaimana dengan seri Cray? Jangankan ILP64, LP64, LLP64 yang normal. (Saya selalu berpikir microsoft berpura-pura dengan LLP64, seharusnya P64.) Saya pasti bisa membayangkan sebuah komite mencoba untuk menutupi semua pangkalan ...
sumber
Menyiratkan bahwa intptr_t harus selalu menggantikan size_t dan sebaliknya.
sumber