Kapan harus menggunakan std :: size_t?


201

Saya hanya ingin tahu apakah saya harus menggunakan std::size_tloop dan sebagainya int? Misalnya:

#include <cstdint>

int main()
{
    for (std::size_t i = 0; i < 10; ++i) {
        // std::size_t OK here? Or should I use, say, unsigned int instead?
    }
}

Secara umum, apa praktik terbaik tentang kapan harus digunakan std::size_t?

Jawaban:


186

Aturan praktis yang baik adalah untuk apa pun yang Anda perlu membandingkan dalam kondisi loop terhadap sesuatu yang secara alami std::size_titu sendiri.

std::size_tadalah jenis sizeofekspresi apa pun dan dijamin dapat mengekspresikan ukuran maksimum objek apa pun (termasuk array apa pun) dalam C ++. Dengan ekstensi itu juga dijamin cukup besar untuk indeks array apa pun sehingga merupakan tipe alami untuk loop demi indeks di atas array.

Jika Anda hanya menghitung hingga angka maka mungkin lebih alami untuk menggunakan jenis variabel yang memegang angka itu atau intatau unsigned int(jika cukup besar) karena ini harus menjadi ukuran alami untuk mesin.


41
Perlu disebutkan bahwa tidak menggunakan size_tsaat Anda dapat menyebabkan bug keamanan .
BlueRaja - Danny Pflughoeft

5
Tidak hanya int "alami", tetapi pencampuran tipe yang ditandatangani dan tidak ditandatangani dapat menyebabkan bug keamanan juga. Indeks yang tidak ditandatangani merupakan masalah yang sulit ditangani dan alasan yang baik untuk menggunakan kelas vektor khusus.
Jo So

2
@ JoSo Ada juga ssize_tuntuk nilai yang ditandatangani.
EntangledLoops

70

size_tadalah tipe hasil dari sizeofoperator.

Gunakan size_tuntuk variabel yang memodelkan ukuran atau indeks dalam array. size_tmenyampaikan semantik: Anda segera tahu itu mewakili ukuran dalam byte atau indeks, bukan hanya bilangan bulat lainnya.

Juga, menggunakan size_tuntuk mewakili ukuran dalam byte membantu membuat kode ini portabel.


32

The size_tjenis dimaksudkan untuk menentukan ukuran dari sesuatu jadi wajar untuk menggunakannya, misalnya, mendapatkan panjang string dan kemudian memproses setiap karakter:

for (size_t i = 0, max = strlen (str); i < max; i++)
    doSomethingWith (str[i]);

Anda memang harus berhati-hati terhadap kondisi batas, karena ini adalah tipe yang tidak ditandatangani. Batas di ujung atas biasanya tidak begitu penting karena maksimum biasanya besar (meskipun adalah mungkin untuk sampai ke sana). Kebanyakan orang hanya menggunakanint untuk hal semacam itu karena mereka jarang memiliki struktur atau susunan yang cukup besar untuk melebihi kapasitas itu int.

Tetapi hati-hati terhadap hal-hal seperti:

for (size_t i = strlen (str) - 1; i >= 0; i--)

yang akan menyebabkan loop tak terbatas karena perilaku pembungkus nilai yang tidak ditandai (meskipun saya telah melihat kompiler memperingatkan terhadap hal ini). Ini juga dapat dikurangi dengan (sedikit lebih sulit untuk dipahami tetapi setidaknya kebal terhadap masalah pembungkus):

for (size_t i = strlen (str); i-- > 0; )

Dengan menggeser penurunan ke efek samping pasca-periksa dari kondisi kelanjutan, ini melakukan pengecekan untuk kelanjutan pada nilai sebelum penurunan, tetapi masih menggunakan nilai yang dikurangi di dalam loop (itulah sebabnya loop berjalan dari len .. 1bukan len-1 .. 0).


14
Ngomong-ngomong, itu adalah praktik yang buruk untuk memanggil strlensetiap iterasi dari satu loop. :) Anda dapat melakukan sesuatu seperti ini:for (size_t i = 0, len = strlen(str); i < len; i++) ...
musiphil

1
Bahkan jika itu adalah tipe yang ditandatangani, Anda harus berhati-hati terhadap kondisi batas, mungkin lebih dari itu karena integer overflow yang ditandatangani adalah perilaku yang tidak terdefinisi.
Adrian McCarthy

2
Menghitung mundur dengan benar dapat dilakukan dengan cara (terkenal):for (size_t i = strlen (str); i --> 0;)
Jo So

1
@ Joo Jadi, itu sebenarnya trik yang cukup rapi meskipun saya tidak yakin saya suka pengenalan -->operator "pergi ke" (lihat stackoverflow.com/questions/1642028/… ). Telah memasukkan saran Anda ke dalam jawabannya.
paxdiablo

Dapatkah Anda melakukan yang sederhana if (i == 0) break;di akhir for for loop (mis for (size_t i = strlen(str) - 1; ; --i). , (Saya lebih suka milik Anda, tetapi hanya ingin tahu apakah ini akan bekerja dengan baik)
RastaJedi

13

Menurut definisi, size_tadalah hasil dari sizeofoperator. size_tdibuat untuk merujuk pada ukuran.

Frekuensi Anda melakukan sesuatu (10, dalam contoh Anda) bukan tentang ukuran, jadi mengapa menggunakan size_t? int, atau unsigned int, harus ok.

Tentu saja juga relevan dengan apa yang Anda lakukan idi dalam loop. Jika Anda meneruskannya ke fungsi yang mengambil unsigned int, misalnya, pilih unsigned int.

Bagaimanapun, saya sarankan untuk menghindari konversi tipe implisit. Buat semua konversi jenis eksplisit.


10

size_tadalah cara yang sangat mudah dibaca untuk menentukan dimensi ukuran item - panjang string, jumlah byte yang dibutuhkan pointer, dll. Ini juga portabel di seluruh platform - Anda akan menemukan bahwa 64bit dan 32bit keduanya berperilaku baik dengan fungsi sistem dan size_t- sesuatu yang unsigned intmungkin tidak dilakukan (misalnya kapan Anda harus menggunakanunsigned long


9

Jawaban singkat:

hampir tidak pernah

jawaban panjang:

Setiap kali Anda perlu memiliki vektor char yang lebih besar 2gb pada sistem 32 bit. Dalam setiap kasus penggunaan lainnya, menggunakan jenis yang ditandatangani jauh lebih aman daripada menggunakan jenis yang tidak ditandatangani.

contoh:

std::vector<A> data;
[...]
// calculate the index that should be used;
size_t i = calc_index(param1, param2);
// doing calculations close to the underflow of an integer is already dangerous

// do some bounds checking
if( i - 1 < 0 ) {
    // always false, because 0-1 on unsigned creates an underflow
    return LEFT_BORDER;
} else if( i >= data.size() - 1 ) {
    // if i already had an underflow, this becomes true
    return RIGHT_BORDER;
}

// now you have a bug that is very hard to track, because you never 
// get an exception or anything anymore, to detect that you actually 
// return the false border case.

return calc_something(data[i-1], data[i], data[i+1]);

Setara yang ditandatangani size_tadalah ptrdiff_t, bukan int. Tetapi menggunakan intmasih jauh lebih baik dalam banyak kasus daripada size_t. ptrdiff_tadalah longpada 32 dan 64 bit sistem.

Ini berarti bahwa Anda selalu harus mengonversi ke dan dari size_t setiap kali Anda berinteraksi dengan std :: wadah, yang tidak terlalu indah. Tetapi pada konferensi asli yang sedang berjalan para penulis c ++ menyebutkan bahwa merancang std :: vector dengan size_t yang tidak ditandatangani adalah kesalahan.

Jika kompiler memberi Anda peringatan tentang konversi implisit dari ptrdiff_t ke size_t, Anda bisa membuatnya eksplisit dengan sintaks konstruktor:

calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);

jika hanya ingin mengulang koleksi, tanpa batas cheking, gunakan rentang berdasarkan untuk:

for(const auto& d : data) {
    [...]
}

di sini beberapa kata dari Bjarne Stroustrup (penulis C ++) akan asli

Bagi sebagian orang, kesalahan desain yang ditandatangani / tidak ditandatangani di STL ini adalah alasan yang cukup, untuk tidak menggunakan std :: vector, melainkan implementasi sendiri.


1
Saya mengerti dari mana mereka berasal, tetapi saya masih berpikir itu aneh untuk menulis for(int i = 0; i < get_size_of_stuff(); i++). Sekarang, tentu saja, Anda mungkin tidak ingin melakukan banyak loop mentah, tetapi - ayolah, Anda juga menggunakannya.
einpoklum

Satu-satunya alasan saya menggunakan loop mentah, adalah karena pustaka algoritma c ++ dirancang dengan sangat buruk. Ada bahasa, seperti Scala, yang memiliki perpustakaan yang jauh lebih baik dan lebih berkembang untuk beroperasi pada koleksi. Maka kasus penggunaan loop mentah cukup banyak dihilangkan. Ada juga pendekatan untuk meningkatkan c ++ dengan STL yang baru dan lebih baik, tetapi saya ragu ini akan terjadi dalam dekade mendatang.
Arne

1
Saya mendapatkan unsigned i = 0; menegaskan (i-1, MAX_INT); tapi saya tidak mengerti mengapa Anda mengatakan "jika saya sudah mengalami underflow, ini menjadi benar" karena perilaku aritmatika pada int unsigned selalu didefinisikan, yaitu. hasilnya adalah hasil modulo ukuran integer representable terbesar. Jadi jika saya == 0, maka saya-- menjadi MAX_INT dan kemudian i ++ menjadi 0 lagi.
mabraham

@mabraham saya melihat dengan hati-hati, dan Anda benar, kode saya bukan yang terbaik untuk menunjukkan masalahnya. Biasanya ini x + 1 < ysetara dengan x < y - 1, tetapi mereka tidak dengan bilangan bulat unsigend. Itu dapat dengan mudah memperkenalkan bug ketika hal-hal ditransformasikan yang dianggap setara.
Arne

8

Gunakan std :: size_t untuk mengindeks / menghitung array gaya-C.

Untuk wadah STL, Anda akan memiliki (misalnya) vector<int>::size_type , yang harus digunakan untuk mengindeks dan menghitung elemen vektor.

Dalam praktiknya, keduanya biasanya int yang tidak ditandatangani, tetapi tidak dijamin, terutama ketika menggunakan pengalokasi khusus.


2
Dengan gcc di linux, std::size_tbiasanya unsigned long(8 byte pada sistem 64 bit) daripada unisgned int(4 byte).
rafak

5
Array gaya-C tidak diindeks oleh size_tkarena indeks dapat negatif. Seseorang dapat menggunakan size_tuntuk contoh arraynya sendiri jika tidak ingin menjadi negatif.
Johannes Schaub - litb

Apakah perbandingan pada u64s secepat perbandingan pada u32s? Saya telah menghitung hukuman performa yang berat untuk menggunakan u8 dan u16 sebagai loop sentinel, tetapi saya tidak tahu apakah Intel mendapatkan aksinya bersama pada usia 64-an.
Crashworks

2
Karena pengindeksan array gaya-C sama dengan menggunakan operator +pada pointer, tampaknya itulah ptrdiff_tyang digunakan untuk indeks.
Pavel Minaev

8
Adapun vector<T>::size_type(dan juga untuk semua kontainer lainnya), itu sebenarnya agak tidak berguna, karena secara efektif dijamin size_t- seperti yang telah ditentukan Allocator::size_type, dan untuk pembatasan yang berkaitan dengan kontainer lihat 20.1.5 / 4 - khususnya, size_typeharus menjadi size_t, dan difference_typeharus ptrdiff_t. Tentu saja, standarnya std::allocator<T>memenuhi persyaratan itu. Jadi hanya menggunakan lebih pendek size_tdan tidak repot-repot dengan sisa dari banyak :)
Pavel Minaev

7

Segera sebagian besar komputer akan menjadi arsitektur 64-bit dengan OS 64-bit: ini menjalankan program yang beroperasi pada wadah miliaran elemen. Maka Anda harus menggunakan size_tbukan intsebagai indeks loop, jika indeks Anda akan membungkus di elemen 2 ^ 32: th, baik pada sistem 32-dan 64-bit.

Bersiaplah untuk masa depan!


Argumen Anda hanya berlaku sejauh yang dibutuhkan seseorang long intdaripada int. Jika size_trelevan pada OS 64-bit, itu juga relevan pada OS 32-bit.
einpoklum

4

Saat menggunakan size_t berhati-hatilah dengan ungkapan berikut

size_t i = containner.find("mytoken");
size_t x = 99;
if (i-x>-1 && i+x < containner.size()) {
    cout << containner[i-x] << " " << containner[i+x] << endl;
}

Anda akan mendapatkan false dalam ekspresi if terlepas dari nilai apa yang Anda miliki untuk x. Butuh beberapa hari bagi saya untuk menyadari hal ini (kodenya sangat sederhana sehingga saya tidak melakukan tes unit), walaupun hanya butuh beberapa menit untuk mencari tahu sumber masalahnya. Tidak yakin lebih baik melakukan gips atau menggunakan nol.

if ((int)(i-x) > -1 or (i-x) >= 0)

Kedua cara harus bekerja. Inilah pengujian saya

size_t i = 5;
cerr << "i-7=" << i-7 << " (int)(i-7)=" << (int)(i-7) << endl;

Output: i-7 = 18446744073709551614 (int) (i-7) = - 2

Saya ingin komentar orang lain.


2
harap perhatikan bahwa itu (int)(i - 7)adalah underflow yang dilemparkan ke intsetelahnya, sementara int(i) - 7itu bukan underflow sejak pertama kali Anda dikonversi imenjadi int, dan kemudian kurangi 7. Selain itu saya menemukan contoh Anda membingungkan.
hochl

Maksud saya adalah int biasanya lebih aman ketika Anda melakukan pengurangan.
Kemin Zhou

4

size_t dikembalikan oleh berbagai pustaka untuk menunjukkan bahwa ukuran wadah itu bukan nol. Anda menggunakannya saat Anda kembali sekali: 0

Namun, dalam contoh Anda di atas perulangan pada size_t adalah bug potensial. Pertimbangkan yang berikut ini:

for (size_t i = thing.size(); i >= 0; --i) {
  // this will never terminate because size_t is a typedef for
  // unsigned int which can not be negative by definition
  // therefore i will always be >= 0
  printf("the never ending story. la la la la");
}

penggunaan bilangan bulat yang tidak ditandatangani memiliki potensi untuk menciptakan jenis masalah yang halus ini. Oleh karena itu saya juga lebih suka menggunakan size_t hanya ketika saya berinteraksi dengan kontainer / jenis yang memerlukannya.


Everone tampaknya menggunakan size_t di loop tanpa peduli tentang bug ini, dan saya belajar ini dengan cara yang sulit
Pranjal Gupta

-2

size_t adalah jenis yang tidak ditandatangani yang dapat menyimpan nilai integer maksimum untuk arsitektur Anda, sehingga terlindung dari bilangan bulat integer karena tanda (masuk 0x7FFFFFFF bertambah 1 akan memberi Anda -1) atau ukuran pendek (int pendek tanpa tanda 0xFFFF ditambah 1 akan memberi Anda 0).

Hal ini terutama digunakan dalam pengindeksan array / loop / aritmatika alamat dan sebagainya. Fungsi suka memset()dan sama size_thanya menerima , karena secara teoritis Anda mungkin memiliki blok memori ukuran2^32-1 (pada platform 32bit).

Untuk loop sederhana seperti itu jangan repot-repot dan gunakan hanya int.


-3

size_t adalah tipe integral yang tidak ditandatangani, yang dapat mewakili integer terbesar pada sistem Anda. Hanya gunakan itu jika Anda membutuhkan array yang sangat besar, matriks, dll.

Beberapa fungsi mengembalikan size_t dan kompiler Anda akan memperingatkan Anda jika Anda mencoba melakukan perbandingan.

Hindari itu dengan menggunakan tipe data yang ditandatangani / tidak ditandatangani yang sesuai atau cukup ketik untuk hack cepat.


4
Hanya gunakan jika Anda ingin menghindari bug dan lubang keamanan.
Craig McQueen

2
Ini mungkin sebenarnya tidak dapat mewakili integer terbesar di sistem Anda.
Adrian McCarthy

-4

size_t tidak ditandatangani int. jadi kapan pun Anda ingin int unsigned Anda dapat menggunakannya.

Saya menggunakannya ketika saya ingin menentukan ukuran array, counter dll ...

void * operator new (size_t size); is a good use of it.

10
Sebenarnya itu tidak harus sama dengan unsigned int. Ini tidak ditandatangani, tetapi mungkin lebih besar (atau saya kira lebih kecil meskipun saya tidak tahu platform apa pun di mana ini benar) daripada sebuah int.
Todd Gamblin

Misalnya, pada mesin 64 bit size_tmungkin bilangan bulat 64 bit yang tidak ditandatangani, sedangkan pada mesin 32 bit hanya bilangan bulat 32 bit.
HerpDerpington
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.