Dizin Değişkenlerinin Türünü Seçme


11

Çoğu zaman dizin değişkenlerini temsil eden Tamsayı türü kullanırız. Ancak bazı durumlarda,

std::vector<int> vec;
....

for(int i = 0; i < vec.size(); ++i)
....

Bu, derleyicinin imzalı / imzasız değişkenlerin karışık kullanımıyla ilgili uyarıyı yükseltmesine neden olur. dizin değişkenini for( size_t i = 0; i < vec.size(); i++ )(veya an unsigned int) olarak yaparsam sorunları sıralar.

Windows türlerini kullanmak daha spesifik hale geldiğinde, Windows API'larının çoğu DWORD ile uğraşır (imzasız uzun olarak yazılan).

Benzer yinelemeyi kullandığımda yine aynı uyarıya neden olur. Şimdi şöyle yazarsam

DWORD dwCount;
....

for(DWORD i = 0; i < dwCount; ++i)
....

Bunu biraz garip buluyorum. Algılardaki sorun olabilir.

Endeks değişkenleri ile aralık problemlerinin oluşmasını önlemek için aynı tip indeks değişkenini kullanmamız gerektiğini kabul ediyorum. Örneğin;

_int64 i64Count; // 
....

for(_int64 i = 0; i < i64Count; ++i)
....

Ancak DWORD veya imzasız tamsayıların yeniden yazılmasında herhangi bir sorun var mı?

for(int i = 0; (size_t)i < vec.size(); ++i)

İnsanların çoğu benzer sorunlarla çalışıyor?


4
Dizini temsil etmek için neden imzalı bir tamsayı kullanasınız? Bu, bir dizeyi saklamak için tamsayıların vektörünü kullanmak gibidir.
Šimon Tóth

3
@Let_Me_Be: Çünkü sınır koşullarının test edilmesini kolaylaştırıyor. Örneğin, sıfıra kadar geri sayımda, döngü gövdesini çalıştırmadan önce yapılan bir testten daha azı, işaretsiz bir değerle çalışamaz. Benzer şekilde maksimuma kadar olan bir sayı da çalışmaz. Tabii ki bu durumda işaretli bir tam sayı da çalışmaz (çünkü bu kadar büyük bir değeri temsil edemez).
Yttrill

"Bu, derleyicinin imzalı / imzasız değişkenlerin karışık kullanımıyla ilgili uyarıyı yükseltmesine neden olur." Bu, ele almanız gereken iki problemden sadece biri . Birçok durumda std::size_tint'den (hatta uzuntan) daha yüksek bir sıralamadır. Vektörün boyutu aşılırsa std::numeric_limits<int>::max(), int kullandığınızdan pişman olursunuz.
Adrian McCarthy

Yanıtlar:


11

vektör size doğru türü kullanmak için söyleyen bir typedef vardır: -

for(std::vector<int>::size_type i = 0; i < thing.size(); ++i)
{
}

Neredeyse her zaman size_t olarak tanımlanır, ancak buna güvenemezsiniz.


8
Gerçekten okunabilirlikte bir gelişme değil, IMHO.
Doc Brown

Hayır, ancak vektörde bir dizin için kullanılacak doğru türün ne olduğunu söylemenin tek yolu olduğu için, bu gerçekten önemli değil ...
JohnB

4
Bu günlerde c ++ 11 sadece auto kullanın
JohnB

6
@JohnB Şunu mu demek istediniz auto i = 0? Hepsi de yardımı, vermez ibir hale int.
zenith

1
İle okunabilirlik geliştirilebilir using index_t = std::vector<int>::size_type;.
Toby Speight

4
std::vector<int> vec;

for(int i = 0; i < vec.size(); ++i)

Bunun için bir fordöngü değil, bir yineleyici kullanın .

Diğerleri için, sürece değişken türü aynı boyutta olduğu gibi, static_castsadece iyi çalışması gerekir (yani DWORDiçin int16_t)


2
for (std::vector<int>::iterator i = vec.begin(); i != vec.end(); ++i)yazmak bir acıdır. Sahip olmak for (auto i = vec.begin();...çok daha okunabilir. Tabii ki, foreachC ++ 11 de.
David Thornley

3

Açıkladığınız durum C ++ 'da beğenmediğim şeylerden biri. Ama bununla yaşamayı öğrendim,

for( size_t i = 0; i < vec.size(); i++ )

veya

for( int i = 0; i < (int)vec.size(); i++ )

(tabii ki, ikincisi sadece int taşması riski olmadığında).


3

İmzalı ve imzasız arasındaki karşılaştırma konusunda sizi uyarmasının nedeni, imzalı değerin büyük olasılıkla imzasız hale dönüştürülmesidir, bu beklediğiniz gibi olmayabilir.

Örnekte (karşılaştırarak intiçin size_t), intörtük dönüştürülecektir size_t(sürece inther nasılsa daha büyük yelpazesine sahiptir size_t). Bu nedenle, intnegatifse, büyük olasılıkla sarmalama nedeniyle karşılaştırdığınız değerden daha büyük olacaktır. Dizininiz asla negatif değilse bu sorun olmaz, ancak yine de bu uyarıyı alırsınız.

Bunun yerine, işaretsiz türü (örneğin kullanmak unsigned int, size_tolarak, ya da John B önerir , std::vector<int>::size_typeİşaret değişken için):

for(unsigned int i = 0; i < vec.size(); i++)

Geriye sayım yaparken dikkatli olun:

for(unsigned int i = vec.size()-1; i >= 0; i--) // don't do this!

Yukarıdakiler işe yaramaz çünkü imzasız i >= 0olduğunda her zaman doğrudur i. Bunun yerine, geri sayan döngüler için " ok operatörünü " kullanın :

for (unsigned int i = vec.size(); i-- > 0; )
    vec[i] = ...;

Diğer cevapların işaret ettiği gibi, normalde a'yı geçmek için bir yineleyici kullanmak istersiniz vector. İşte C ++ 11 sözdizimi:

for (auto i = vec.begin(); i != vec.end(); ++i)

1
Bu yine unsigned intde boyutu tutacak kadar büyük olmadığı riskini taşır .
Adrian McCarthy

2

C ++ 11 için yeni bir seçenek, aşağıdaki gibi şeyler yapabilirsiniz

for(decltype(vec.size()) i = 0; i < vec.size(); ++i) {...}

ve

for(decltype(dWord) i = 0; i < dWord; ++i) {...}

Temel for-loop'tan biraz daha tekrarlasa da, değerleri belirtmenin 11 öncesi öncesi yöntemleri kadar uzun sürmez ve bu kalıbı tutarlı bir şekilde kullanmak, olmasa bile, mümkün olan çoğu terim için işe yarayacaktır. Karşılaştırma yapmak istiyoruz, bu da kod yeniden düzenleme için harika. Hatta böyle basit durumlarda da çalışır:

int x = 3; int final = 32; for(decltype(final) i = x; i < final; ++i)

Dahası, herhangi bir akıllı değere (gibi ) autoayarladığınızda kullanmanız gerekirken , 0 gibi basit bir tamsayı değişmez olduğu için, otomatik olarak bunu çözecekti .ivec.begin()decltypeint

Dürüst olmak gerekirse, autodöngü artışlarının karşılaştırılan değere bakması için tür belirlemesini genişletmek için bir derleyici mekanizması görmek istiyorum .


1

Ben olduğu gibi int için bir döküm kullanın for (int i = 0; i < (int)v.size(); ++i). Evet, çirkin. Boyutları temsil etmek için işaretsiz tamsayı kullanmaya karar verdikleri standart kütüphanenin aptal tasarımını suçluyorum. (.. için? Aralığı bir bit genişletmek?)


1
Hangi durumda negatif bir şeyden oluşan bir koleksiyon boyutu anlamlı olur? Çeşitli toplama boyutları için işaretsiz tam sayılar kullanmak benim için mantıklı bir seçim gibi görünüyor . Son kontrol ettim, bir ipin uzunluğunu almak nadiren olumsuz bir sonuç verdi ...
CVn

1
Hangi durumda basit bir test if(v.size()-1 > 0) { ... }için boş bir kap için doğruya dönmek gibi anlamlı olur ? Sorun, boyutların aritmetik, esp. imzasız olduklarından dolayı sorun isteyen dizin tabanlı kaplarla. Temel olarak, 1) bitsel manipülasyonlar veya 2) modüler aritmetik dışında herhangi bir şey için işaretsiz tipler kullanmak sorun çıkarmaktadır.
zvrba

2
iyi bir nokta. Özel örneğinizin if(v.size() > 1) { ... }amacını gerçekten görmeme rağmen (muhtemelen amacı daha net hale getirdiğinden yazarım ve ek bir bonus olarak imzalı / imzasız meselesi geçersiz olur), bazı özel durumlarda nasıl olduğunu görüyorum imza yararlı olabilir. Ben düzeltilmiş duruyorum.
CVn

1
@Michael: Katılıyorum örnek oldu. Yine de, genellikle iç içe döngülerle algoritmalar yazarım: for (i = 0; i <v.size () - 1; ++ i) için (j = i + 1; j <v.size (); ++ j) .. v boşsa, dış döngü (size_t) -1 kez yürütülür. Bu yüzden ya döngüden önce v.empty () kontrol etmek ya da her ikisi de ben şahsen çirkin geçici çözümler olduğunu düşünüyorum imzalı bir tür için v.size () döküm gerekir. Ben daha az LOC olduğu gibi bir döküm seçin, hayır () s => hata için daha az olasılık. (Ayrıca, 2. tamamlayıcıda, dönüşüm aşırı akışı negatif bir sayı döndürür, bu nedenle döngü hiç yürütülmez.)
zvrba

Aralığı 1 bit genişletmek, 16 bit sistemlerde çok kullanışlıdır (ve olmaya devam etmektedir).
Adrian McCarthy
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.