Saya semakin bingung dengan size_t
di C. saya tahu bahwa itu dikembalikan oleh sizeof
operator. Tapi apa sebenarnya itu? Apa itu tipe data?
Let's mengatakan saya memiliki untuk
loop:
for(i = 0; i < some_size; i++)
Saya harus menggunakan int i;
atau size_t aku;
?
Menurut 1999 ISO C standar (C99),
size_t
adalah aplikasi yang unsigned integer tipe setidaknya 16 bit (lihat bagian 7.17 dan 7.18.3).
size_t
adalah sebuah tipe data unsigned didefinisikan oleh beberapa C/C++ standar, misalnya C99 ISO/IEC 9899 standar, yang didefinisikan dalamstddef.jam
.1 Hal ini dapat menjadi lebih diimpor oleh masuknyastdlib.h
sebagai file ini secara internal sub termasukstddef.jam
.jenis Ini digunakan untuk mewakili ukuran dari sebuah objek. Fungsi perpustakaan yang mengambil atau mengembalikan ukuran mengharapkan mereka untuk menjadi tipe atau jenis kembali dari
size_t
. Selanjutnya, yang paling sering digunakan compiler berbasis operator sizeof harus mengevaluasi untuk nilai konstan yang kompatibel dengansize_t
.
Sebagai implikasinya, size_t
adalah jenis yang dijamin untuk menahan setiap indeks array.
size_t
adalah sebuah tipe unsigned. Jadi, itu tidak mewakili nilai-nilai negatif(<0). Anda menggunakannya saat anda sedang menghitung sesuatu, dan yakin bahwa hal itu tidak dapat menjadi negatif. Misalnya, strlen()
kembali size_t
karena panjang tali minimal harus 0.
Dalam contoh anda, jika anda loop indeks akan selalu lebih besar dari 0, itu mungkin masuk akal untuk menggunakan size_t
, atau tipe data unsigned.
Ketika anda menggunakan size_t
objek, anda harus memastikan bahwa dalam semua konteks yang digunakan, termasuk aritmatika, anda ingin non-nilai negatif. Misalnya, let's mengatakan anda memiliki:
size_t s1 = strlen(str1);
size_t s2 = strlen(str2);
dan anda ingin menemukan perbedaan dari panjang str2
dan str1
. Anda tidak bisa melakukan:
int diff = s2 - s1; /* bad */
Hal ini karena nilai yang diberikan untuk diff
selalu akan menjadi angka positif, bahkan ketika s2 < s1
, karena perhitungan dilakukan dengan unsigned jenis. Dalam hal ini, tergantung pada apa yang anda menggunakan kasus ini, anda mungkin akan lebih baik menggunakan int
(atau lama
) untuk s1
dan s2
.
Ada beberapa fungsi di C/POSIX yang bisa/harus menggunakan size_t
, tapi don't karena alasan historis. Misalnya, parameter kedua untuk fgets
idealnya size_t
, tapi int
.
size_t
adalah tipe yang dapat menampung setiap indeks array.
Tergantung pada pelaksanaan, hal ini dapat menjadi salah satu dari:
unsigned char
unsigned pendek
unsigned int
unsigned long
unsigned lama
Berikut ini's bagaimana size_t
didefinisikan dalam stddef.h
mesin saya:
typedef unsigned long size_t;
Jika anda adalah empiris tipe,
echo | gcc -E -xc -include 'stddef.h' - | grep size_t
Output untuk Ubuntu 14.04 64-bit GCC 4.8:
typedef long unsigned int size_t;
Perhatikan bahwa stddef.h
disediakan oleh GCC dan tidak glibc bawah src/gcc/ginclude/stddef.h
di GCC 4.2.
Menarik C99 penampilan
malloc
mengambil size_t
sebagai argumen, sehingga hal ini menentukan ukuran maksimum yang dapat dialokasikan.Dan sejak itu juga dikembalikan oleh ukuran
, saya pikir itu batas ukuran maksimum dari setiap array.
Lihat juga: https://stackoverflow.com/questions/9386979/the-maximum-size-of-an-array-in-c
Karena ada yang belum disebutkan, yang utama linguistik makna dari size_t
adalah bahwa ukuran
operator mengembalikan sebuah nilai dari tipe tersebut. Demikian juga, primer pentingnya ptrdiff_t
adalah bahwa mengurangkan satu pointer dari yang lain akan menghasilkan sebuah nilai dari tipe tersebut. Fungsi perpustakaan yang menerima itu melakukannya karena itu akan memungkinkan fungsi-fungsi tersebut bekerja dengan benda-benda yang ukurannya melebihi UINT_MAX pada sistem di mana benda-benda seperti itu bisa ada, tanpa memaksa penelepon untuk limbah kode lewat sebuah nilai yang lebih besar dari "unsigned int" pada sistem jenis yang lebih besar akan cukup untuk semua benda yang mungkin.
size_t
dan int
tidak dapat dipertukarkan. Misalnya pada 64-bit Linux size_t
adalah 64-bit dalam ukuran (yaitu sizeof(void*)
) tapi int
adalah 32-bit.
Juga perhatikan bahwa size_t
unsigned. Jika anda membutuhkan versi ditandatangani kemudian ada ssize_t
pada beberapa platform dan itu akan menjadi lebih relevan untuk anda contoh.
Sebagai aturan umum, saya akan menyarankan menggunakan int
untuk sebagian besar kasus-kasus umum dan hanya menggunakan size_t
/ssize_t
ketika ada kebutuhan khusus untuk itu (dengan mmap()
misalnya).
Untuk pergi ke mengapa size_t
yang diperlukan untuk hidup dan bagaimana kita sampai di sini:
Secara pragmatis, size_t
dan ptrdiff_t
dijamin akan 64 bit lebar pada 64-bit pelaksanaan, 32 bit lebar 32-bit implementasi, dan sebagainya. Mereka tidak bisa memaksa setiap jenis yang ada untuk maksud itu, pada setiap compiler, tanpa melanggar kode legacy.
A size_t
atau ptrdiff_t
belum tentu sama sebagai sebuah intptr_t
atau uintptr_t
. Mereka berbeda pada beberapa arsitektur yang masih di gunakan ketika size_t
dan ptrdiff_t
ditambahkan ke Standar di akhir '80-an, dan menjadi usang ketika C99 menambahkan banyak baru jenis tapi tidak hilang lagi (seperti Windows 16-bit). X86 dalam 16-bit protected mode telah tersegmentasi memori dimana kemungkinan terbesar array atau struktur bisa hanya 65.536 byte dalam ukuran, tetapi yang jauh
pointer yang dibutuhkan untuk menjadi 32 bit lebar, lebih lebar dari register. Pada orang-orang, intptr_t
akan menjadi 32 bit lebar tapi size_t
dan ptrdiff_t
bisa 16 bit lebar dan masuk dalam daftar. Dan siapa yang tahu apa jenis sistem operasi yang mungkin akan ditulis di masa depan? Dalam teori, i386 arsitektur menawarkan 32-bit segmentasi model dengan 48-bit pointer bahwa tidak ada sistem operasi yang telah pernah benar-benar digunakan.
Jenis offset memori tidak bisa menjadi lama
karena terlalu banyak warisan kode mengasumsikan bahwa lama
adalah persis 32 bit lebar. Asumsi ini bahkan dibangun ke dalam UNIX dan Windows Api. Sayangnya, banyak warisan kode juga diasumsikan bahwa seorang lama
yang cukup lebar untuk mengadakan pointer, file offset, jumlah detik yang sudah berlalu sejak 1970, dan sebagainya. POSIX sekarang menyediakan cara standar untuk memaksa kedua asumsi untuk menjadi kenyataan bukan mantan, tapi baik adalah portabel asumsi untuk membuat.
Itu tidak bisa int
karena hanya segelintir kecil dari penyusun di tahun 90-an membuat int
64 bit lebar. Maka mereka benar-benar mendapat aneh dengan menjaga lama
32 bit lebar. Revisi berikutnya dari Standar dinyatakan ilegal untuk int
untuk menjadi yang lebih luas dari lama
, tapi int
masih 32 bit lebar pada kebanyakan sistem 64-bit.
Itu tidak bisa long long int
, yang pula ditambahkan kemudian, sejak itu diciptakan untuk menjadi setidaknya 64 bit yang luas bahkan pada sistem 32-bit.
Jadi, jenis baru yang dibutuhkan. Bahkan jika tidak, semua orang jenis lain berarti sesuatu yang lain dari offset dalam array atau object. Dan jika ada satu pelajaran dari kegagalan dari 32 ke 64-bit migrasi, itu harus spesifik tentang sifat-sifat apa yang diperlukan untuk memiliki, dan tidak menggunakan salah satu yang dimaksud hal-hal yang berbeda di program yang berbeda.
Secara umum, jika anda adalah dimulai dari 0 dan pergi ke atas, selalu menggunakan tipe unsigned untuk menghindari overflow membawa anda ke nilai negatif situasi. Hal ini sangat penting, karena jika anda menginap batas-batas yang terjadi kurang dari max loop anda, tetapi anda loop max terjadi menjadi lebih besar dari max dari jenis anda, anda akan membungkus negatif dan anda mungkin mengalami kesalahan segmentasi (SIGSEGV). Jadi, secara umum, tidak pernah menggunakan int loop dimulai dari 0 dan pergi ke atas. Menggunakan aplikasi yang unsigned.
size_t atau setiap tipe unsigned dapat dilihat digunakan sebagai loop variabel sebagai variabel loop biasanya lebih besar dari atau sama dengan 0.
Ketika kita menggunakan size_t objek, kita harus memastikan bahwa dalam semua konteks yang digunakan, termasuk aritmatika, kita ingin hanya non-nilai negatif. Sebagai contoh, program berikut pasti akan memberikan hasil yang tak terduga:
// C program to demonstrate that size_t or
// any unsigned int type should be used
// carefully when used in a loop
#include<stdio.h>
int main()
{
const size_t N = 10;
int a[N];
// This is fine
for (size_t n = 0; n < N; ++n)
a[n] = n;
// But reverse cycles are tricky for unsigned
// types as can lead to infinite loop
for (size_t n = N-1; n >= 0; --n)
printf("%d ", a[n]);
}
Output
Infinite loop and then segmentation fault