İmzasız girişlerle ilgili en iyi uygulamalar nelerdir?


43

Her yerde imzasız girişler kullanıyorum ve yapmam gerekip gerekmediğinden emin değilim. Bu, veritabanı birincil anahtar kimliği sütunlarından sayaçlara vb. Olabilir. Bir sayı asla negatif olmamalıysa, her zaman imzasız bir int kullanacağım.

Ancak başkalarının kodundan kimsenin bunu yapmadığını fark ettim. Gözden kaçmam için çok önemli bir şey var mı?

Düzenleme: Bu sorudan beri, C'de hatalar için negatif değerler döndürmenin, C ++ 'da olduğu gibi istisnalar atmak yerine yaygın olduğunu fark ettim.


26
Dikkat et for(unsigned int n = 10; n >= 0; n --)(sonsuz döngüler)
Chris Burt-Brown

3
C ve C ++ 'da işaretsiz girişler tam olarak taşma davranışını tanımlamıştır (modulo 2 ^ n). İmzalı girişler yapmaz. İyileştiriciler, tanımlanmamış taşma davranışlarından giderek daha fazla yararlanıyor ve bazı durumlarda şaşırtıcı sonuçlara yol açıyor.
Steve314

2
İyi soru! Ben de bir kez uint t kısıtlama aralığı kullanmak için cazip ama risk / rahatsızlık herhangi bir yarar / kolaylıktan daha ağır basar buldum. Sizin de söylediğiniz gibi çoğu kütüphane bir yardımcının yapacağı düzenli kayıtları kabul eder. Bu, çalışmayı zorlaştırıyor, ancak şu soruyu da akla getiriyor: Buna değer mi? Uygulamada (şeyler hakkında aptalca bir şekilde gitmediğinizi varsayarak), olumlu bir kişinin beklendiği yerde nadiren -218 değerine sahip olacaksınız. Bu -218 bir yerden gelmiş olmalı, değil mi? ve kaynağını takip edebilirsiniz. Nadiren olur. Size yardımcı olmak için iddialar, istisnalar, kod sözleşmelerinden yararlanın.
İş

@William Ting: Bu sadece C / C ++ ile ilgiliyse, sorunuza uygun etiketleri eklemelisiniz.
CesarGon

2
@Chris: Sonsuz döngü problemi gerçekte ne kadar önemli? Demek istediğim, eğer serbest bırakılırsa, kod açıkça test edilmedi. Bu hatayı ilk yaptığınızda hata ayıklamak için birkaç saate ihtiyacınız olsa bile, ikinci seferde kodunuz dönmeyi bırakmadığında ne arayacağınızı bilmelisiniz.
Güvenli

Yanıtlar:


28

Gözden kaçmam için çok önemli bir şey var mı?

Hesaplamalar hem imzalı hem de imzasız türleri ve farklı boyutları içerdiğinde, tür tanıtımı için kurallar karmaşık olabilir ve beklenmeyen davranışlara yol açabilir .

Java'nın imzasız int türlerini ihmal etmesinin ana nedeni olduğuna inanıyorum.


3
Başka bir çözüm de, numaralarınızı uygun şekilde elle almanızı istemek olacaktır. Go'nun yaptığı gibi görünüyor (onunla sadece birazcık oynadım) ve Java'nın yaklaşımından daha çok hoşlanıyorum.
Tikhon Jelvis

2
Bu, Java'nın 64 bit işaretsiz türünü içermemesinin iyi bir nedeni ve belki de 32 bit işaretsiz türünü içermemesinin iyi bir nedeni oldu. böyle bir işlem sadece 64 bit işaretli bir sonuç vermelidir]. Ancak imzasız türler intbu tür bir zorluğa yol açmayacaktır, ancak (herhangi bir hesaplamanın yapacağı için int); İmzasız bir bayt türünün eksikliği hakkında söyleyecek iyi bir şeyim yok.
supercat,

17

Michael'ın geçerli bir noktaya sahip olduğunu düşünüyorum ama IMO herkesin sürekli olarak int kullanmasının nedeni (özellikle de for (int i = 0; i < max, i++) bu şekilde öğrendik. Bir ' programlama nasıl öğrenilir ' kitabındaki her bir örnek intbir fordöngüde kullanıldığında, çok azı bu uygulamayı sorgulayacaktır.

Diğer sebep ise int% 25 daha kısa uint, ve hepimiz tembeliz ... ;-)


2
Eğitim konusuna katılıyorum. Çoğu insan ne okuduğunu asla sorgulamaz: Bir kitaptaysa, yanlış olamaz, değil mi?
Matthieu M.

1
Muhtemelen ++, belirli davranışının nadiren gerekli olmasına rağmen, döngü endeksi bir yineleyici veya temel olmayan başka bir tür (veya derleyici gerçekten yoğunsa) kopyalar üzerinde anlamsız çalkalamalara yol açabilmesine rağmen, herkesin postfix kullanırken postfix kullanması da muhtemeldir. .
underscore_d

Sadece "for (uint i = 10; i> = 0; --i)" gibi bir şey yapmayın. Döngü değişkenleri için sadece ints kullanmak bu olasılığı önler.
David Thornley


8

İmzalı ve imzasız türleri karıştırmak sizi acı dünyasına götürebilir. Tüm imzasız türleri kullanamazsınız çünkü negatif sayılar içeren geçerli bir aralığa sahip olan veya bir hatayı belirtmek için bir değere ihtiyaç duyan ve -1 en doğal olan şeylerle karşılaşırsınız. Böylece net sonuç, birçok programcının tüm işaretli tamsayı tiplerini kullanmasıdır.


1
Belki aynı değişkende geçerli değerleri hata göstergesiyle karıştırmamak ve bunun için ayrı değişkenler kullanmak daha iyi bir uygulamadır. Verilmiş, C standart kütüphanesi burada iyi bir örnek değil.
Güvenli

7

Benim için türler iletişim ile ilgili. Açıkça işaretsiz bir int kullanarak, imzalı değerlerin geçerli değerler olmadığını söylersiniz. Bu, değişken adına ek olarak kodunuzu okurken biraz bilgi eklememe izin verir. İdeal olarak ben isimsiz bir tip daha fazla şey söyleyeceğim, ama bana her yerde kullanmış olduğunuzdan daha fazla bilgi verir.

Maalesef, herkes kodlarının nasıl iletişim kurduğu konusunda çok bilinçli değil ve bu değerler en azından imzasız olsalar bile, muhtemelen her yerde her yeri görmenizin nedenidir.


4
Ancak değerlerimi bir ay boyunca yalnızca 1 ile 12 arasında sınırlamak isteyebilirim. Bunun için başka bir tür kullanıyor muyum? Peki ya bir ay? Bazı diller aslında bunun gibi değerleri kısıtlamaya izin verir. Net / C # gibi diğerleri Kod Sözleşmelerini sağlar. Tabii ki, negatif olmayan tamsayılar oldukça sık görülür, ancak bu türü destekleyen dillerin çoğu daha fazla kısıtlamayı desteklemez. Öyleyse, kişi bir hata ve hata kontrolü karışımı mı kullanmalı, yoksa her şeyi hata kontrolüyle mi yapmalı? Çoğu kütüphane nerede kullanmanın mantıklı olacağı hakkında bir şey istemez, bu nedenle birini kullanmak ve döküm yapmak sakıncalıdır.
İş

@Job Aylarınızda bir çeşit derleyici / tercüman tarafından uygulanan kısıtlama kullanmanız gerektiğini söyleyebilirim. Size kurmanız için bazı kazanlar verebilir, ancak gelecek için hatayı önleyen ve beklediğiniz şeyi daha net ileten zorlu bir kısıtlamaya sahipsiniz. Hataları önlemek ve iletişimi kolaylaştırmak, uygulama sırasındaki rahatsızlıktan çok daha önemlidir.
daramarak

1
"Değerlerimi bir ay boyunca yalnızca 1 ile 12 arasında sınırlamak isteyebilirim" "Ay gibi sınırlı bir değer kümeniz varsa, ham tamsayıları değil bir numaralandırma türünü kullanmalısınız.
Josh Caswell

6

Kullandığım unsigned intçoğunlukla, dizi indeksleri için C ++ ve 0'dan başlayan herhangi sayaç için bunu açıkça "bu değişken negatif olamaz" demek iyi olduğunu düşünüyorum.


14
Muhtemelen size bunun için size_t kullanıyor olmalısınız c ++
JohnB

2
Biliyorum, sadece rahatsız edilemiyorum.
quant_dev

3

İmzalı bir int sınırına yaklaşabilecek veya sınırlarını aşabilecek bir tamsayı ile uğraşırken bunu umursamalısınız. 32 bit tamsayının pozitif maksimum değeri 2.147.483.647 olduğundan, a) asla negatif olmayacağını ve b) 2.147.483.648'e ulaşabileceğini biliyorsanız, işaretsiz bir int kullanmalısınız. Çoğu durumda, veritabanı anahtarları ve sayıcılar dahil, bu tür sayılara bile yaklaşmayacağım, bu nedenle işaret bitinin sayısal bir değer için mi kullanıldığına veya işaretini belirtmekle ilgilenme konusunda endişelenmekle uğraşmıyorum.

Söyleyeceğim: imzasız bir int'ye ihtiyacınız olmadığını bilmediğiniz sürece int kullanın.


2
Maksimum değerlere ulaşabilecek değerlerle çalışırken, işaretten bağımsız olarak tamsayı taşma işlemlerini kontrol etmeye başlamalısınız. Bu kontroller genellikle imzasız tipler için daha kolaydır, çünkü çoğu işlem tanımsız ve uygulama tanımlanmış davranış olmadan iyi tanımlanmış sonuçlara sahiptir.
Güvenli

3

Basitlik ve güvenilirlik arasında bir değişim. Derleme zamanında yakalanabilecek hatalar arttıkça yazılım da güvenilir olur. Farklı insanlar ve kuruluşlar bu spektrum boyunca farklı noktalardadır.

Ada'da herhangi bir yüksek güvenilirlik programlaması yaparsanız, metre cinsinden uzaklık - metre cinsinden mesafe gibi değişkenler için bile farklı türler kullanırsınız ve yanlışlıkla birini diğerine atadığınızda derleyici onu işaretler. Bu, güdümlü bir füzeyi programlamak için mükemmeldir, ancak bir web formunu onaylıyorsanız fazladan (pun amaçlanan). Gereksinimlere uygun olduğu sürece hiçbir şekilde yanlış bir şey olması gerekmez.


2

Joel Etherton'ın akıl yürütmesine katılmaya meyilliyim, ama tam tersi bir sonuca vardım. Gördüğüm gibi, sayıların imzalı bir türün sınırlarına hiç yaklaşma ihtimalinin düşük olduğunu bilseniz bile , negatif sayıların gerçekleşmeyeceğini biliyorsanız , o zaman bir türün işaretli varyantını kullanmak için çok az neden vardır.

Aynı sebepten dolayı, birkaç seçilmiş örnekte, SQL Server tablolarında (32 BIGINTbit tamsayı) yerine (64 bit tamsayı) kullandım INTEGER. Verilerin herhangi bir makul süre içinde 32-bit sınırına ulaşma olasılığı küçüktür, ancak gerçekleşirse, bazı durumlarda sonuçlar oldukça yıkıcı olabilir. Dilleri doğru bir şekilde yerleştirdiğinizden emin olun, yoksa yolun çok aşağısındaki ilginç gariplikle sonuçlanacaksınız ...

Bununla birlikte, imzalanmış veya imzasız veritabanı birincil anahtar değerleri gibi bazı şeyler için gerçekten önemli değil, çünkü kırık verileri veya bu satırlardaki bir şeyi manuel olarak onaramazsanız, hiçbir zaman doğrudan değerle uğraşmazsınız; bu bir tanımlayıcı, başka bir şey değil. Bu gibi durumlarda, tutarlılık büyük olasılıkla, kesin imza seçiminden daha önemlidir. Aksi halde, imzalanmış bazı yabancı anahtar sütunlarla ve imzasız olanlarla, belirgin bir şekli olmayan - veya bu ilginç garipliği tekrar edersiniz.


Bir SAP sisteminden alınan verilerle çalışıyorsanız, Kimlik alanları için BIGINT'i (Müşteri Numarası, MakaleNumarası vb.) Şiddetle tavsiye ederim. Hiç kimse ID olarak alfanümerik dizgileri kullanmadığı sürece, bu ...
çeker

1

Dış alanda kısıtlı veri depolama ve veri değişim bağlamları dışında birinin imzalı türleri kullanmasını öneririm. 32 bit işaretli bir tamsayının çok küçük olacağı ancak 32 bit işaretsiz bir değerin bugün için yeterli olacağı çoğu durumda, 32 bit işaretsiz değerin de yeterince büyük olmaması çok uzun sürmez.

Birinin işaretsiz türleri kullanması gereken birincil zamanlar, birinin birden fazla değeri daha büyük bir değere birleştirmesi (örn. Dört baytı 32 bit sayıya dönüştürme) veya daha büyük değerleri daha küçük değerlere ayırma (örn. 32 bitlik bir sayıyı dört bayt olarak saklama) ) veya birinin periyodik olarak "yuvarlanması" beklenen ve bununla başa çıkması gereken bir miktar olduğunda (meskun olan bir konut sayacını düşünün; çoğu, okumalar arasında devrilmediğinden emin olmak için yeterli haneye sahiptir) yılda üç kez okundukları, ancak ölçüm aletinin kullanım ömrü boyunca devrilmediklerinden emin olmak için yeterli değilse). İmzasız tipler çoğu zaman sadece anlambilimlerinin gerekli olduğu durumlarda kullanılması gereken yeterli 'tuhaflığa' sahiptir.


1
“Tavsiye ederim [...] genellikle imzalı türleri kullanmalı.” Hm, imzalı türlerin avantajlarından bahsetmeyi unuttun ve yalnızca imzasız türlerin ne zaman kullanılacağının bir listesini verdin. "gariplik" ? İmzasız işlemlerin çoğu iyi tanımlanmış davranış ve sonuçlara sahip olsa da, imzalı türleri (taşma, bit kaydırma, ...) kullanırken tanımsız ve uygulama tanımlı davranışlar girersiniz. Burada tuhaf bir "tuhaflık" tanımı var.
Güvenli

1
@ Emin: Başvurduğum "tuhaflık", özellikle karışık imzalı ve imzasız türleri içeren işlemlerde, karşılaştırma operatörlerinin anlamlarıyla ilgili. Taşan kadar büyük değerler kullanılırken imzalı türlerin davranışının tanımsız olduğu doğrudur, ancak göreceli olarak küçük sayılarla çalışırken bile imzasız türlerin davranışı şaşırtıcı olabilir. Örneğin, (-3) + (1u) -1'den büyüktür. Ayrıca, sayılar için geçerli olan bazı normal matematiksel ilişkisel ilişkiler imzasızlar için geçerli değildir. Örneğin, (ab)> c, (ac)> b anlamına gelmez.
supercat,

1
@Secure: Kişi "büyük" işaretli numaralarla her zaman bu tür bir ilişkisel davranışa güvenemeyeceği doğru olsa da, davranışlar, imzalanan tam sayıların alanına göre "küçük" sayılarla çalışırken beklendiği gibi çalışır. Buna karşılık, yukarıda belirtilen birliktelik imzasız değerleri "2 3 1" olan problemlidir. Bu arada, imzalı davranışların sınırların dışında kullanıldığında tanımsız davranışlara sahip olması, yerel kelime boyutundan daha küçük değerler kullanıldığında bazı platformlarda gelişmiş kod oluşturulmasına izin verebilir.
supercat,

1
Bu yorumlar ilk etapta cevabınızda olsaydı, hiçbir öneride bulunmadan bir öneri ve "isim arama" yerine, yorum yapmadım. ;) Hala burada "tuhaflık" ile aynı fikirde olmasam da, bu sadece türün tanımı. Verilen iş için doğru aleti kullanın ve tabii ki aleti öğrenin. +/- ilişkilerine ihtiyacınız olduğunda imzasız tipler yanlış araç. size_tİmzasız olmasının ve imzalanmasının bir nedeni var ptrdiff_t.
Güvenli

1
@ Emin: Eğer biri bir bit dizisini temsil etmek istiyorsa, işaretsiz türler mükemmeldir; Sanırım orada aynı fikirdeyiz. Bazı küçük mikroplarda imzasız tipler, sayısal miktarlar için daha verimli olabilir. Ayrıca, deltaların sayısal büyüklükleri temsil ettiği durumlarda da faydalıdır, ancak gerçek değerler yoktur (örneğin, TCP sıra numaraları). Öte yandan, herhangi biri işaretsiz değerleri çıkardığı zaman, sayılar küçük olsa bile köşe durumlarda hakkında endişelenmek zorundadır; İmzalı değerleri olan bu tür matematik yalnızca sayıların büyük olduğu durumlarda köşe durumlarda sunar.
supercat,

1

Kodumu ve amacını daha net hale getirmek için imzasız girdiler kullanıyorum. Hem imzalanmış hem de imzasız türlerle aritmetik işlem yaparken beklenmedik örtük dönüşümlere karşı korunmak için yaptığım bir şey, imzasız değişkenlerim için imzasız kısa (genellikle 2 bayt) kullanmak. Bu birkaç nedenden dolayı etkilidir:

  • İmzasız kısa değişkenleriniz ve değişmezler (int türündeki) veya int türündeki değişkenlerle aritmetik yaptığınızda, bu, imzasız değişkenin ifadeyi değerlendirmeden önce her zaman bir int değerine yükseltilmesini sağlar; . Bu, ifadenin sonucunun elbette imzalı bir int değerine uyduğu varsayılarak, imzalı ve imzasız türlerle aritmetik işlem yapan herhangi bir beklenmeyen davranışı önler.
  • Kullandığınız imzasız değişkenler çoğu zaman, imzasız 2 baytlık kısa bir değerin (65,535) maksimum değerini aşmaz

Genel ilke, imzasız değişkenlerinizin türünün, imzalı türe promosyonu sağlamak için imzalı değişkenlerin türünden daha düşük bir sıraya sahip olması gerektiğidir. O zaman beklenmedik taşma davranışına sahip olmazsınız. Açıkçası bunu her zaman sağlayamazsınız, ancak (çoğu) bunu sağlamak çoğu zaman mümkündür.

Örneğin, son zamanlarda bazı döngü için böyle bir şey vardı:

const unsigned short cuint = 5;
for(unsigned short i=0; i<10; ++i)
{
    if((i-2)%cuint == 0)
    {
       //Do something
    }
}

'2' değişmezi int türündedir. İmzasız bir kısa yerine imzasız bir int olsaydım, o zaman alt ifadede (i-2) 2 işaretsiz bir int olarak terfi ederdi (çünkü imzasız int imzalı int'den daha yüksek önceliğe sahipti). Eğer i = 0 ise, alt-ifade eşittir (0u-2u) = taşma nedeniyle bazı büyük değerler. İ = 1 ile aynı fikir. Ancak, işaretsiz bir kısa olduğu için, int imzalı olan '2' kelimesiyle aynı tipte terfi eder ve her şey yolunda gider.

Daha fazla güvenlik için: Uyguladığınız mimarinin 2 byte olmasına neden olduğu nadir durumlarda, bu, işaretsiz kısa değişkenin uyuşmadığı durumlarda aritmetik ifadedeki her iki işlenin de imzasız int'ye yükseltilmesine neden olabilir İkincisi en fazla 32,767 <65,535 değerinde olan imzalı 2 baytlık int. (Daha fazla ayrıntı için bkz. Https://stackoverflow.com/questions/17832815/c-implicit-conversion-signed-unsigned ). Buna karşı korunmak için programınıza aşağıdaki gibi bir static_assert ekleyebilirsiniz:

static_assert(sizeof(int) == 4, "int must be 4 bytes");

int 2 byte olan mimarilerde derlenmez.

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.