Std :: size_t ne zaman kullanılır?


201

Merak ediyorum std::size_tbunun yerine döngüler ve şeyler için kullanmalıyım int? Örneğin:

#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?
    }
}

Genel olarak, ne zaman kullanılacağına ilişkin en iyi uygulama std::size_tnedir?

Yanıtlar:


186

İyi bir kural, döngü durumunda doğal olarak kendiliğinden bir şeyle karşılaştırmanız gereken her şey içindir std::size_t.

std::size_therhangi bir sizeofifadenin türüdür ve C ++ 'da herhangi bir nesnenin (herhangi bir dizi dahil) maksimum boyutunu ifade edebileceği garanti edilir. Ek olarak, herhangi bir dizi dizini için yeterince büyük olması garanti edilir, bu nedenle bir dizi üzerinden bir dizine göre döngü için doğal bir türdür.

Sadece bir sayıya kadar sayıyorsanız, bu sayıyı tutan değişkenin türünü veya bir intveya unsigned int(yeterince büyükse) makinenin doğal bir boyutu olması gerektiği için kullanmak daha doğal olabilir.


41
It yetmeyecek söz değil kullanarak size_tsize yol açabilir gerekirken güvenlik hataları .
BlueRaja - Danny Pflughoeft

5
İnt "doğal" olmakla kalmaz, aynı zamanda imzalı ve imzasız tiplerin karıştırılması da güvenlik hatalarına neden olabilir. İmzasız endeksler, ele alınması gereken bir acıdır ve özel bir vektör sınıfı kullanmak için iyi bir nedendir.
Jo So

2
@JoSo ssize_tİşaretli değerler için de vardır .
EntangledLoops

70

size_tsizeofoperatörün sonuç türüdür .

size_tBir dizideki boyutu veya dizini modelleyen değişkenler için kullanın . size_tanlambilimi iletir: hemen başka bir tamsayıdan ziyade bayt veya bir dizinde bir boyutu temsil ettiğini bilirsiniz.

Ayrıca, size_tbayt cinsinden bir boyutu temsil etmek, kodun taşınabilir olmasına yardımcı olur.


32

size_tTipi belirtmek içindir boyutunu bir dize uzunluğunu almak ve daha sonra her bir karakteri işlenmesi, örneğin, kullanmak doğal yüzden bir şey:

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

Sen do it işaretsiz tip beri, elbette sınır koşulları için dikkat etmek gerekir. (Olsa maksimum genellikle büyük olduğundan en sonunda sınır genellikle o önemli değildir olduğunu gidilir mümkün). Çoğu insan sadece intbu tür bir şey için kullanır, çünkü nadiren bunun kapasitesini aşacak kadar büyük olan yapılara veya dizilere sahiptirler int.

Ama şuna dikkat et:

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

(imzasız değerlerin kaydırma davranışı nedeniyle sonsuz bir döngüye neden olacak). Bu ayrıca hafifletilebilir (anlaşılması biraz daha zor, ancak en azından sarma problemlerine karşı bağışıklık):

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

Devam durumunun bir sonrası kontrol yan yürürlüğe eksiltme kaydırılmasıyla, bu değer üzerinde devam etmesi için denetim yapar önce (bu nedenle ilmek çalışır azalma, ama yine de döngü içinde azalır değerini kullanır len .. 1yerine len-1 .. 0).


14
Bu arada, strlenbir döngünün her yinelemesini çağırmak kötü bir uygulamadır . :) Böyle bir şey yapabilirsiniz:for (size_t i = 0, len = strlen(str); i < len; i++) ...
musiphil

1
İmzalı bir tür olsa bile, sınır koşullarına dikkat etmeniz gerekir, belki de daha da fazlası, imzalı tamsayı taşması tanımsız davranış olduğundan.
Adrian McCarthy

2
Doğru geri sayım aşağıdaki (rezil) şekilde yapılabilir:for (size_t i = strlen (str); i --> 0;)
Jo So

1
@JoSo, -->"gider" operatörünün tanıtımını sevdiğimden emin olmadığım halde aslında oldukça düzgün bir numara (bkz. Stackoverflow.com/questions/1642028/… ). Önerinizi cevaba dahil ettiniz.
paxdiablo

if (i == 0) break;For döngüsünün sonunda bir basit yapabilir misiniz (örneğin for (size_t i = strlen(str) - 1; ; --i). (Ben seninkini daha iyi seviyorum, ama sadece bunun da işe
yarayıp

13

Tanımı gereği, operatörün size_tsonucudur sizeof. size_tboyutlara atıfta bulunmak için oluşturulmuştur.

Bir şeyi kaç kez yaptığınız (örneğinizde 10) boyutlarla ilgili değil, neden kullanıyorsunuz size_t? intveya unsigned inttamam olmalıdır.

Tabii ki idöngü içinde ne yaptığınız da önemlidir . unsigned intÖrneğin, bir işlev alan bir işleve iletirseniz unsigned int.

Her durumda, örtük tür dönüşümlerinden kaçınmanızı öneririm. Tüm tür dönüşümlerini açık yapın.


10

size_tbir öğenin boyut boyutunu belirtmek için çok okunabilir bir yoldur - bir dizenin uzunluğu, bir işaretçinin aldığı bayt miktarı, vb. Ayrıca platformlar arasında taşınabilir - 64bit ve 32bit'in hem sistem işlevleriyle iyi davrandığını hem de size_t- bir şey unsigned intmesela kullanmanız gereken zaman ne olmayabilir (unsigned long


9

kısa cevap:

neredeyse hiç

uzun cevap:

32 bitlik bir sistemde 2GB'dan daha büyük bir char vektörüne ihtiyacınız olduğunda. Diğer her kullanım durumunda, imzalı bir tür kullanmak, imzasız bir tür kullanmaktan çok daha güvenlidir.

misal:

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]);

İmzalı eşdeğer size_tolduğunu ptrdiff_t, değil int. Ancak intkullanımı çoğu durumda size_t boyutundan daha iyidir. ptrdiff_tolduğu long32 ve 64 bit sistemlerde.

Bu, çok güzel olmayan bir std :: kaplarıyla her etkileşim kurduğunuzda, size_t boyutuna ve on'dan dönüştürmeniz gerektiği anlamına gelir. Ancak devam eden yerel bir konferansta c ++ yazarları işaretsiz bir size_t ile std :: vector tasarlamanın bir hata olduğunu belirtti.

Derleyiciniz size ptrdiff_t ile size_t arasındaki örtük dönüşümlerle ilgili uyarılar verirse, bunu yapıcı sözdizimi ile açık hale getirebilirsiniz:

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

sadece bir koleksiyonu yinelemek istiyorsanız, sınırlama olmadan, aşağıdakileri temel alan aralığı kullanın:

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

burada yerli gitmek Bjarne Stroustrup (C ++ yazar) bazı kelimeler

Bazı insanlar için, STL'deki bu imzalı / imzasız tasarım hatası, std :: vector'u kullanmak yerine kendi uygulamasını kullanmak için yeterlidir.


1
Nereden geldiklerini anlıyorum, ama hala yazmanın garip olduğunu düşünüyorum for(int i = 0; i < get_size_of_stuff(); i++). Şimdi, elbette, çok fazla ham döngü yapmak istemeyebilirsiniz, ama - hadi, onları da kullanıyorsunuz.
einpoklum

Ham döngüler kullanmamın tek nedeni, c ++ algoritma kitaplığının oldukça kötü tasarlanmış olmasıdır. Koleksiyonlar üzerinde çalışmak için çok daha iyi ve daha gelişmiş bir kütüphaneye sahip Scala gibi diller var. Sonra ham döngülerin kullanım durumu hemen hemen ortadan kaldırılır. Ayrıca c ++ 'ı yeni ve daha iyi bir STL ile geliştirmeye yönelik yaklaşımlar da var, ancak bunun önümüzdeki on yıl içinde gerçekleşeceğinden şüpheliyim.
Arne

1
İmzasız i = 0 olsun; iddia (i-1, MAX_INT); ama neden imzasız ints aritmetik davranışı her zaman tanımlanmıştır, çünkü "zaten bir taşma varsa, bu olur" dediğini anlamıyorum. sonuç, temsil edilebilen en büyük tamsayının büyüklüğü olan modülondur. Eğer i == 0 ise, i-- MAX_INT olur ve i ++ tekrar 0 olur.
mabraham

@mabraham Dikkatle baktım ve haklısın, kodum sorunu göstermek için en iyisi değil. Normalde bu x + 1 < yeşdeğerdir x < y - 1, ancak gönderilmemiş tamsayılarla değildir. Bu, eşdeğer olduğu kabul edilen şeyler dönüştürüldüğünde kolayca hatalar oluşturabilir.
Arne

8

C stili dizileri indeksleme / sayma için std :: size_t kullanın.

STL kapları için, (örneğin) vector<int>::size_typevektör öğelerini indeksleme ve sayma için kullanılması gerekir.

Pratikte, ikisi de imzasız ints'tır, ancak özellikle özel ayırıcılar kullanırken garanti edilmez.


2
Linux üzerinde gcc ile, (4 bayt) yerine std::size_tgenellikle unsigned long(64 bit sistemlerde 8 unisgned intbayt).
rafak

5
C-tarzı diziler yine de endekslenmez size_t, çünkü indeksler negatif olabilir. size_tOlumsuz olmak istemiyorsa, kişi böyle bir dizinin kendi örneği için kullanılabilir .
Johannes Schaub - litb

U64'lerin karşılaştırmaları u32'lerin karşılaştırmaları kadar hızlı mı? U8'leri ve u16'ları döngü nöbetçileri olarak kullanmak için ciddi performans cezaları zamanladım, ancak Intel'in 64'lerde birlikte hareket edip etmediğini bilmiyorum.
Crashworks

2
C-tarzı dizi indeksleme, +işaretçiler üzerinde operatörün kullanımına eşdeğer olduğundan, endeksler için kullanılacak gibi görünmektedir ptrdiff_t.
Pavel Minaev

8
Gelince vector<T>::size_typeetkili olması sağlanır, çünkü (ve diğer tüm konteynerler için aynen), o aslında oldukça yararsız olduğunu size_t- bu kadar typedef 'oluyor Allocator::size_typeözellikle - ve saygı ile bu konuda kısıtlamalar için konteynerler 20.1.5 / 4'ü görmek için size_typezorunluluk olmak size_tve difference_typeolması gerekir ptrdiff_t. Elbette, varsayılan std::allocator<T>bu gereksinimleri karşılar. Bu yüzden sadece daha kısa kullanın ve size_tpartinin geri kalanıyla uğraşmayın :)
Pavel Minaev

7

Yakında bilgisayarların çoğu, milyarlarca öğenin kaplarında çalışan programları çalıştıran 64 bit OS: es ile 64 bit mimariler olacak. Sonra gerekir kullanmak size_tyerine int, döngü endeksi olarak aksi Dizininizin edecek etrafında sarın 2 ^ 32 de: Her iki 32 ve 64 bit sistemlerde, inci öğesi.

Geleceğe hazırlanın!


Argümanınız sadece kişinin andan long intziyade ihtiyacı olduğu kadar ileri gider int. Eğer size_t64-bit işletim sisteminde alakalı bir 32 bit OS üzerinde tıpkı alakalı oldu.
einpoklum

4

Size_t kullanırken aşağıdaki ifadeye dikkat edin

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;
}

X için hangi değere sahip olursanız olun, if ifadesinde false alırsınız. Sorunun kaynağını anlamak sadece birkaç dakika sürse de, bunu fark etmem birkaç gün sürdü (kod o kadar basit ki birim testi yapmadım). Oyuncu kadrosu yapmanın veya sıfır kullanmanın daha iyi olduğundan emin değilim.

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

Her iki yol da işe yaramalıdır. İşte benim test çalışmam

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

Çıktı: i-7 = 18446744073709551614 (int) (i-7) = - 2

Başkalarının yorumlarını istiyorum.


2
notu lütfen (int)(i - 7)artığını bir boşalma olduğunu intiken, sonradan int(i) - 7ilk dönüştürmek beri bir boşalma değil ibir etmek intve sonra çıkarmak 7. Ayrıca örneğinizi kafa karıştırıcı buldum.
hochl

Demek istediğim, çıkarma yaptığınızda int genellikle daha güvenlidir.
Kemin Zhou

4

size_t, bu kabın boyutunun sıfır olmadığını belirtmek için çeşitli kütüphaneler tarafından döndürülür. Bir kez geri geldiğinde kullanıyorsun: 0

Ancak, yukarıdaki örnekte size_t üzerinde döngü yapmak potansiyel bir hatadır. Aşağıdakileri göz önünde bulundur:

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");
}

imzasız tamsayıların kullanılması, bu tür küçük sorunlar yaratma potansiyeline sahiptir. Bu nedenle imho Ben sadece gerektiren kaplar / türleri ile etkileşimde size_t kullanmayı tercih ederim.


Everone bu hatayı rahatsız etmeden döngüde size_t kullanıyor gibi görünüyor ve bunu zor yoldan öğrendim
Pranjal Gupta

-2

size_tmimariniz için maksimum tamsayı değerini tutabilen imzasız bir türdür, bu nedenle işaret ( 0x7FFFFFFF1 ile arttırılan imzalı size 1 verir) veya kısa boyut (1 ile arttırılmış imzasız kısa int 0xFFFF size verecek) nedeniyle tamsayı taşmalarından korunur 0).

Genellikle dizi indeksleme / döngüler / adres aritmetiği vb. Teorik olarak büyüklükte bir hafıza bloğuna sahip olabilirsiniz (32bit platformunda) memset()ve benzerleri gibi işlevler size_tsadece kabul eder 2^32-1.

Böyle basit döngüler için rahatsız etmeyin ve sadece int kullanın.


-3

size_t, sisteminizdeki en büyük tamsayıyı temsil edebilen işaretsiz bir integral türüdür. Sadece çok büyük dizilere, matrislere vb. İhtiyacınız varsa kullanın.

Bazı işlevler size_t döndürür ve karşılaştırmalar yapmaya çalışırsanız derleyiciniz sizi uyarır.

Uygun bir imzalı / imzasız veri türü kullanarak veya hızlı bir kesmek için basitçe tahmin kullanarak kaçının.


4
Sadece hataları ve güvenlik deliklerini önlemek istiyorsanız kullanın.
Craig McQueen

2
Aslında sisteminizdeki en büyük tamsayıyı temsil edemeyebilir.
Adrian McCarthy

-4

size_t imzasız int. imzasız int istediğinizde kullanabilirsiniz.

Ben dizi boyutu, sayaç vb belirtmek istediğinizde kullanın ...

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

10
Aslında imzasız int ile aynı değildir. Bu ise imzasız ama büyük olabilir (ya da ben bu doğrudur herhangi platformların bilmiyorum ama daha küçük tahmin) int den.
Todd Gamblin

Örneğin, 64 bitlik bir makinede size_tişaretsiz 64 bitlik bir tamsayı olabilirken, 32 bitlik bir makinede yalnızca 32 bitlik işaretsiz bir tamsayı olabilir.
HerpDerpington
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.