Modern C de değişken genişlik tipleri sabit tiplerle değiştirildi mi?


21

Bugün Kod İnceleme üzerine bir incelemede ilginç bir noktaya rastladım . @Veedrac, bu cevapta , değişken ebatlı tiplerin (örneğin intve long) uint64_tve gibi sabit ebatlı tiplerle değiştirilmesini önermiştir uint32_t. Bu cevabın yorumlarından alıntı:

İnt ve long (ve dolayısıyla alabilecekleri) boyutları platforma bağlıdır. Öte yandan, int32_t her zaman 32 bit uzunluğundadır. İnt kullanmak, kodunuzun farklı platformlarda farklı şekilde çalıştığı anlamına gelir; bu genellikle istediğiniz şey değildir.

Standardın ardındaki ortak tiplerin sabitlenmemesinin nedeni burada kısmen süperkatçı tarafından açıklanmıştır. C, genellikle o zamanlar sistem programlaması için kullanılan montajın aksine, mimariler arasında taşınabilir olması için yazılmıştır.

Tasarım amacının aslında int dışındaki her türün, çeşitli ebatlardaki sayıları idare edebilecek en küçük şey olduğu ve +/- 32767'yi kaldırabilecek en pratik "genel amaçlı" ebat olması olduğunu düşünüyorum.

Bana gelince, her zaman kullandım intve alternatifler hakkında gerçekten endişelenmedim. Her zaman hikayenin sonu, en iyi performansa sahip en tip olduğunu düşündüm. Sabit genişliğin sabit olacağını düşündüğüm tek yer veri depolamak veya bir ağ üzerinden transfer etmek için kodlama yapmak. Sabit genişlik türlerini nadiren başkaları tarafından yazılmış kodlarda da gördüm.

70'li yıllarda sıkışıp mı kaldım ya da intC99 ve sonrası dönemde kullanmak için bir neden var mı?


1
İnsanların bir kısmı sadece başkalarını taklit ediyor. Sabit bit tipli kodun çoğunluğunun bilinçsiz yapıldığına inanıyorum. Boyutunu ayarlamak için hiçbir sebep yok. Öncelikle 16 bit platformunda (MS-DOS ve 80'lerin X-DOS ve Xenix'i) yapılmış, bugün sadece herhangi bir 64'ü derleyip çalıştıran ve yeni sözcük boyutunun ve adreslemesinin faydalarını sadece derleyen kodum var. Başka bir deyişle, verileri almak / vermek için seri hale getirme işleminin taşınabilir olması için çok önemli bir mimari tasarım olduğu söylenebilir.
Luciano

Yanıtlar:


7

uint32_tProgramcıların büyüklüğü hakkında endişelenmek zorunda kalmamaları gibi tipleri olan ortak ve tehlikeli bir efsane vardır int. Standartlar Komitesinin makineden bağımsız anlambilimsel tamsayıları bildirmek için bir araç tanımlaması yararlı olsa da, işaretsiz tiplerde uint32_tkodun hem temiz hem de taşınabilir bir biçimde yazılmasına izin vermeyecek kadar gevşek olan anlambilim türleri ; ayrıca, imzalı türler, int32gereksiz yere sıkıca tanımlanmış birçok uygulama yolu için anlambilimine sahiptir ve bu nedenle başka türlü yararlı optimizasyonların yapılmasını engeller.

Örneğin, şunları göz önünde bulundurun:

uint32_t upow(uint32_t n, uint32_t exponent)
{
  while(exponent--)
    n*=n;
  return n;
}

int32_t spow(int32_t n, uint32_t exponent)
{
  while(exponent--)
    n*=n;
  return n;
}

Makinelerde intya 4294967295 tutamam, veya 18446744065119617025 tutabilir, ilk fonksiyon tüm değerleri için tanımlanacak nve exponentve davranışı büyüklüğü ile etkilenmez int; ayrıca, standart, int bazı değerleri olan makinelerde farklı davranışlar göstermesini gerektirmeyecek nve exponentbununla birlikte, bunun 4294967295'in intancak 18446744065119617025 olarak gösterilemediği makinelerde Tanımsız Davranışa neden olmasına neden olacaktır .

İkinci işlev, 4611686014132420609 numaralı belgeye sahip olmayan makinelerin bazı değerleri için nve exponentüzerinde tanımsız Davranış intverecek, ancak yapabileceği tüm makinelerde nve tüm değerlerinde tanımlanmış davranış exponentgetirecektir ( int32_tbu iki makinenin tamamlayıcı sarma davranışını belirtir. küçüktür int).

Tarihsel olarak, Standart derleyicilerin inttaşması ile ne yapması gerektiği konusunda hiçbir şey söylemese de upow, derleyiciler taşmayacak intkadar büyüktü gibi aynı davranışı tutarlı bir şekilde vermiştir . Ne yazık ki, bazı yeni derleyiciler, Standart tarafından zorunlu olmayan davranışları ortadan kaldırarak programları "optimize etmek" isteyebilir.


3
El ile uygulamak isteyen herkes powbu kodu unutma, sadece bir örnek ve hitap etmiyor exponent=0!
Mark Hurd

1
Önek azaltma işlecini posta kodu değil de kullanmanız gerektiğini düşünüyorum, şu anda 1 ekstra çarpma yapıyor. Örneğin exponent=1, çek kontrolden önce gerçekleştirilirse, düşüş kontrolden önce yapıldıysa, işlem kontrolden sonra yapıldığı için n bir defa çarpılır. yani - ekseni), çarpma yapılmayacak ve n'nin kendisi döndürülecektir.
ALXGTV

2
@MarkHurd: İşlev aslında adlandırıldığından beri isimlendirilmiştir, N^(2^exponent)ancak formun hesaplamaları, üstelleştirme işlevlerinin hesaplanmasında N^(2^exponent)sıklıkla kullanılır ve mod-4294967296 üstelleştirme, iki dizenin birleştirme işleminin hesaplanması gibi şeyler için kullanışlıdır. hash'ler bilinmektedir.
supercat

1
@ALXGTV: İşlev, güçle ilgili bir şeyi hesaplayan bir şeyin açıklayıcısıydı. Aslında hesapladığı şey, verimli bir şekilde hesaplanan N ^ üssünün bir parçası olan N ^ (2 ^ üs), N küçük olsa bile iyi başarısız olabilir ( uint32_t31'lik bir tekrarlanan çarpma hiç UB vermez, ancak verimlidir). hesaplama 31 ^ N, 31 ^ (2 ^ N) olan hesapların yapılmasını gerektirir, yani
supercat

Bunun iyi bir tartışma olduğunu sanmıyorum. Amaç, tüm girdiler için tanımlanmış, mantıklı olsun ya da olmasın işlevleri yapmak değildir; boyutlar ve taşma hakkında sebep olabilir. int32_tbazen taşma tanımlamış ve bazen değil, bu sizin bahsetmiş olduğunuz şey değildir, ilk etapta taşmayı önleme nedenini bana bildirdiğine göre asgari önem taşır. Ve eğer tanımlanmış taşma yapmak istiyorsanız, sonuç modülünün bir sabit değer elde etmesini istiyorsunuz - yani yine de sabit genişlikte türleri kullanıyorsunuz.
Veedrac

4

Tampon boyutları, dizi dizinleri ve Windows gibi işaretçilerle (ve dolayısıyla adreslenebilir bellek miktarlarıyla) yakından ilgili değerler için, lParammimariye bağlı boyutta bir tamsayı türü olması mantıklıdır. Bu nedenle, değişken boyutlu türler hala yararlıdır. Bu bizim typedefs yüzden size_t, ptrdiff_t, intptr_tvb Bunlar var yerleşik C tiplerinin tamsayı hiçbiri işaretçi boyutlu gerekirse çünkü typedefs olmak.

Soru Yani gerçekten olsun char, short, int, long, ve long longhala yararlıdır.

IME, C ve C ++ programlarının intçoğu şey için kullanması hala yaygındır . Ve çoğu zaman (örneğin, numaralarınız ± 32 767 aralığındayken ve zorlu performans gereksinimleriniz olmadığında), bu işe yarar.

Peki ya 17-32 bit aralığında sayılarla çalışmanız gerekiyorsa (büyük şehirlerin popülasyonu gibi)? Kullanabilirsiniz int, ancak bu bir platform bağımlılığını zorlaştırıyor olabilir. Eğer kesinlikle standarda uymak istiyorsanız long, en az 32 bit olacağı garantili olanı kullanabilirsiniz .

Sorun, C standardının bir tamsayı tipi için herhangi bir maksimum boyut belirtmemesidir . long64 bit olan ve bellek kullanımınızı iki katına çıkaran uygulamalar vardır . Ve eğer bunlar longmilyonlarca maddeden oluşan bir dizinin elemanları ise, hatıraları deliler gibi mahvedeceksiniz.

Bu nedenle, programınızın hem platformlar arası hem de bellek açısından verimli olmasını istiyorsanız, burada intne longuygun bir tür kullanılmalıdır. Girin int_least32_t.

  • I16L32 derleyiciniz, 32-bit verir long;int
  • I32L64 derleyiciniz size int64 bitlik boşa harcanan hafızadan kaçınarak 32 bitlik bir veri sunuyor long.
  • I36L72 derleyiciniz size 36 bit verir int

OTOH, büyük sayılara veya büyük dizilere ihtiyacın olmadığını ama hıza ihtiyacın olduğunu varsayalım . Ve inttüm platformlarda yeterince büyük olabilir, ancak mutlaka en hızlı tip olması gerekmez: 64 bit sistemler genellikle 32 bit olur int. Ama kullanabilirsiniz int_fast16_tve onu olsun, “en hızlı” türü olsun int, longya da long long.

Yani, türleri için pratik kullanım durumları vardır <stdint.h>. Standart tamsayı türleri hiçbir şey ifade etmiyor. Özellikle long, 32 veya 64 bit olabilir ve derleyici yazarlarının kaprisine bağlı olarak bir imleci tutacak kadar büyük olabilir veya olmayabilir.


Gibi türlerle ilgili bir sorun, uint_least32_tdiğer türlerle etkileşimlerinin, bunlardan bile daha zayıf belirtilmiş olmasıdır uint32_t. IMHO, Standart , türü tanımlayan herhangi bir derleyicinin , 32 bit olsaydı , terfi ettiği durumlarda esasen aynı durumlarda imzasız bir tür olarak tanıtması gerektiği uwrap32_tve türlerini tanımlaması gerektiği gibi türler tanımlamalı ve unum32_ttürünü tanımlayan herhangi bir derleyicinin , temel aritmetik promosyonlar her zaman değerini tutabilecek imzalı bir türe dönüştürür. uwrap32_tintunum32_t
supercat,

Buna ek olarak, standart ayrıca, depolama ve yumuşatma ile uyumlu idi türleri tanımlayabilir intN_tve uintN_t, olan ve tanımlanan davranışlar olacaktır tutarlı olan intN_tve uintN_t, ancak Derleyiciler kendi aralığının dışında değerler tahsis yapılan kod bazı özgürlük elde edecektir [olduğu benzer semantik izin belki amaçlanmıştır uint_least32_t, ancak a uint_least16_tve int32_ta'nın eklenmesi imzalı mı yoksa imzasız bir sonuç mu olacağı gibi belirsizlikler olmadan .
supercat
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.