Bir 'uzun' yasağı mantıklı mı?


109

++ (veya C) dünya bugünün çapraz platform C biz var :

Data model  | short |   int |   long | long long | pointers/size_t  | Sample operating systems
... 
LLP64/IL32P64   16      32      32     64           64                Microsoft Windows (x86-64 and IA-64)
LP64/I32LP64    16      32      64     64           64                Most Unix and Unix-like systems, e.g. Solaris, Linux, BSD, and OS X; z/OS
...

Bugün bunun anlamı, herhangi bir "ortak" (işaretli) tamsayı için intyeterli olacaktır ve muhtemelen C ++ uygulama kodunu yazarken varsayılan tamsayı türü olarak kullanılabilir. Aynı zamanda - mevcut pratik amaçlar için - platformlar arasında tutarlı bir boyuta sahip olacaktır.

Bir kullanım durumu en az 64 bit gerektiriyorsa, bugün kullanabiliriz long long, ancak muhtemelen bitlik belirleyici türlerden birini kullanmak veya __int64tür daha anlamlı olabilir.

Bu longortada kalıyor ve longuygulama kodumuzun kullanımının tamamen yasaklandığını düşünüyoruz .

Bu mantıklılong mıdır, yoksa çapraz platform çalışması gereken modern C ++ (veya C) kodunda kullanmak için bir durum var mı? (platform masaüstü, mobil cihazlar olmakla birlikte, mikrodenetleyiciler, DSP'ler vb. gibi şeyler değildir)


Muhtemelen ilginç arka plan bağlantıları:


14
Uzun süren kütüphaneleri nasıl arayacaksınız?
Ángel,

14
long32 bit garanti etmenin tek yolu budur. int16 bit olabilir, bu yüzden bazı uygulamalar için yeterli değil. Evet, intmodern derleyicilerde bazen 16 bit var. Evet, insanlar mikrodenetleyiciler üzerinde yazılım yazarlar. Daha fazla insanın Arduinos vb yükselişini saymıyorum iPhone ve Android cihazların yükselişi ile PC'de daha mikro denetleyici üzerindeki fazla kullanıcıya sahip yazılım yazmak iddia ediyorum
slebetman

53
Neden char, short, int, long ve long long yasaklamıyor ve [u] intXX_t türlerini kullanmıyorsunuz?
immibis

7
@slebetman Biraz daha derine indiğimde, C ++ standardının §3.9.1.3'te gizlenmiş olmasına rağmen, hala yerinde olduğu anlaşılıyor: “İmzalı ve işaretsiz tamsayı tipleri, C standardı, Bölüm 5.2'de verilen kısıtlamaları yerine getirmelidir. 4.2.1." Ve §5.2.4.2.1'deki C standardında, tam olarak sizin yazdığınız gibi minimum aralığı belirtir. Kesinlikle haklıydın. :) Görünüşe göre C ++ standardının bir kopyasına sahip olmak yeterli değil, C standardının kopyasını da bulmak gerekiyor.
Tommy Andersen

11
intHala 16 bit olan DOSBox / Turbo C ++ dünyasını kaçırıyorsunuz . Söylemekten nefret ediyorum, ancak “bugünün platformlar arası dünyası” hakkında yazacaksanız, tüm Hindistan alt kıtasını görmezden gelemezsiniz.
Orbit'teki Hafiflik Yarışları

Yanıtlar:


17

longBugün kullanmamın tek nedeni , onu kullanan harici bir arayüzü çağırmak veya uygulamaktır.

Kısa ve int yayınlarınızda söylediğiniz gibi, bugün tüm büyük masaüstü / sunucu / mobil platformlarda oldukça istikrarlı özelliklere sahip ve bunun öngörülebilir gelecekte değişmesi için hiçbir neden göremiyorum. Bu yüzden genel olarak onlardan kaçınmak için çok az sebep görüyorum.

longÖte yandan bir karmaşa var. Tüm 32-bit sistemlerde şu özelliklere sahip olduğunun farkındayım.

  1. Tam olarak 32 bit boyutundaydı.
  2. Hafıza adresiyle aynı boyuttaydı.
  3. Normal bir sicilde tutulabilen ve tek bir komutla çalışılabilecek en büyük veri birimi ile aynı boyuttaydı.

Bu özelliklerin bir veya daha fazlasına dayanarak büyük miktarda kod yazılmıştır. Ancak 64-bit'e geçişle hepsini korumak mümkün olmadı. Unix benzeri platformlar, karakteristik 1'in maliyeti 2 ve 3'ü koruyan LP64'e gitti. Win64, karakteristik 2 ve 3'ün maliyeti karşılığında karakteristik 1'i koruyan LLP64'e gitti. Sonuç, artık bu özelliklerin hiçbirine güvenemezsiniz. ve bu IMO kullanmak için çok az sebep bırakıyor long.

Tam olarak 32 bit boyutunda bir tür istiyorsanız, kullanmanız gerekir int32_t.

İşaretçi ile aynı boyutta bir tür istiyorsanız, kullanmanız gerekir intptr_t(veya daha iyisi uintptr_t).

Tek bir kayıt / talimatta çalışılabilecek en büyük öğe olan bir tür istiyorsanız, ne yazık ki standardın bir sağladığını sanmıyorum. size_ten yaygın platformlarda doğru olmalı, ancak x32'de olmayacaktı .


PS

"Hızlı" ya da "en az" türlerle uğraşmazdım. "En az" türler yalnızca, nerede mimarileri gizlemekte olan taşınabilirliği önemsiyorsanız önemlidir CHAR_BIT != 8. Pratikte "hızlı" türlerin büyüklüğü oldukça keyfi görünüyor. Linux, en azından x86-64 ve arm64 gibi hızlı 32-bit desteğine sahip 64-bit platformlarda aptalca olan pointer ile aynı boyutta görünmektedir. IIRC iOS onları mümkün olduğunca küçük yapar. Diğer sistemlerin ne yaptığından emin değilim.


PPS

Kullanmanın bir nedeni unsigned long(sade değil long), modulo davranışına sahip olmasının garanti edilmesidir. Ne yazık ki C'nin berbat tanıtım kuralları nedeniyle imzasız tiplerden daha küçük intmodulo davranışları yoktur.

Günümüzde tüm büyük platformlarda uint32_tint ile aynı boyutta veya daha büyüktür ve dolayısıyla modulo davranışına sahiptir. Bununla birlikte, tarihsel olarak var olmuştur ve teorik olarak int64-bit olan ve dolayısıyla uint32_tmodulo davranışının olmadığı gelecekteki platformlarda olabilir .

Şahsen, denklemlerinizin başında "imzasız tipte bir boyut için işe yarayacağından" 1u * "veya" 0u + "kullanarak modulo davranışını zorlama alışkanlığına girmenin daha iyi olacağını söyleyebilirim.


1
"Belirtilen boyut" türlerinin tümü, yerleşik türlerden farklı olan anlambilimi belirtebilselerdi çok daha faydalı olurdu. Örneğin, "int" boyutundan bağımsız olarak mod-65536 aritmetik kullanacak bir tipin yanı sıra, 0 ila 65535 arasında sayıları tutabilen ancak keyfi bir şekilde ve her zaman tutarlı bir şekilde yapamayan bir tipe sahip olmak yararlı olacaktır. bundan daha büyük tutma numaralarının listesi. Çoğu makinede hangi boyut tipinin en hızlı olduğu bağlama bağlı olacaktır, bu nedenle derleyicinin keyfi olarak seçmesine izin vermek hız için en uygun olacaktır.
supercat,

204

Sorunuzda bahsettiğiniz gibi, modern yazılım tamamen internetteki platformlar ve sistemler arasında birlikte çalışma ile ilgilidir. C ve C ++ standartları , belirli boyutlarda değil (Java ve C # gibi dillerin aksine) tamsayı tipi boyutları için aralıklar sunar .

Farklı platformlarda derlenmiş yazılım aynı veri ile aynı şekilde çalıştığından emin olmak için ve diğer yazılım yazılım aynı boyutlarda kullanarak etkileşimde sağlamak için, Sabit boyutlu tamsayılar kullanarak olmalıdır.

<cstdint>Tam olarak bunu sağlayan ve tüm derleyici ve standart kütüphane platformlarının sağlaması gereken standart bir başlık olan Enter . Not: Bu başlık yalnızca C ++ 11'den itibaren gerekliydi, ancak birçok eski kütüphane uygulaması yine de sağladı.

64 bit işaretsiz tam sayı mı istiyorsunuz? Kullanın uint64_t. İmzalanmış 32 bit tam sayı? Kullanın int32_t. Başlıktaki türler isteğe bağlı olsa da, modern platformlar bu başlıkta tanımlanan tüm türleri desteklemelidir.

Bazen, örneğin, diğer sistemlerle iletişim kurmak için kullanılan bir veri yapısında belirli bir bit genişliğine ihtiyaç duyulur. Diğer zamanlarda değil. Daha az katı durumlar için, <cstdint>minimum genişlikte tipler sağlar.

En az değişken var: int_leastXX_tbir tamsayı minimum XX bit tipi olacak. XX bit sağlayan en küçük türü kullanır, ancak türün belirtilen bit sayısından daha büyük olmasına izin verilir. Uygulamada, bunlar tipik olarak, kesin sayıda bit veren yukarıda açıklanan tiplerle aynıdır.

Ayrıca hızlı değişkenler de vardır : int_fastXX_ten az XX bit, ancak belirli bir platformda hızlı performans gösteren bir tür kullanmalıdır. Bu bağlamda "hızlı" tanımı belirtilmemiş. Bununla birlikte, pratikte bu, tipik olarak, bir CPU'nun kayıt büyüklüğünden daha küçük bir türün, CPU'nun kayıt büyüklüğünün bir türüne diğer adı olabileceği anlamına gelir. Örneğin, Visual C ++ 2015'in başlığı int_fast16_t32 bit tamsayı olduğunu belirtir çünkü 32 bit aritmetik x86'da 16 bit aritmetikten daha hızlıdır.

Bunların hepsi önemlidir, çünkü programınızın platformdan bağımsız olarak gerçekleştirdiği hesaplamaların sonuçlarını tutabilecek türleri kullanmanız gerekir. Bir program bir platformda doğru sonuçlar veriyorsa, ancak tam sayı taşmalarındaki farklılıklar nedeniyle bir başkasında yanlış sonuçlar veriyorsa, bu kötüdür. Standart tamsayı türlerini kullanarak, farklı platformlardaki sonuçların kullanılan tamsayıların boyutuna göre aynı olacağını garanti edersiniz (elbette tamsayı genişliğinin yanı sıra platformlar arasında başka farklılıklar olabilir).

Öyleyse evet, longmodern C ++ kodundan yasaklanmalı. Öyle olmalı int, shortve long long.


20
Keşke bunu daha fazla oylayacak beş tane daha hesabım olsaydı.
Steven Burnap

4
+1, bir yapının boyutu, hangi bilgisayarda derleyeceğinize bağlı olduğunda gerçekleşen bazı tuhaf bellek hatalarını ele aldım.
Joshua Snider

9
@Wildcard, C ++ 'ın bir parçası olan bir C başlığıdır: "c" önekine bakınız. Bir C ++ derleme ünitesinde d stdolduğunda , typedef'leri ad alanına koymak için bir yol vardır #include, ancak bağladığım belgeler bundan bahsetmez ve Visual Studio onlara nasıl erişeceğimi önemsemez.

11
Yasaklama int... aşırı olabilir mi? . Kod "uygulaması kodu" için Banning hepsi çok belirsiz belirsiz (ve) platformlarda son derece portatif olması gerekiyorsa (bunu dikkate alacağını bizim devs ile çok iyi oturup olmayabilir.
Martin Ba

5
@Snowman #include <cstdint>edilir gerekli içinde türlerini koymak std::ve (maalesef) isteğe bağlı olarak izin verilen genel ad koymak için de. #include <stdint.h>tam olarak sohbettir. Aynısı diğer C başlıkları çifti için de geçerlidir. Bakınız: stackoverflow.com/a/13643019/2757035 Standardın her birinin yalnızca bazı gerekli uygulamalar tarafından ortaya konan kötü sözleşmelere boyun eğdirmek yerine, kendilerine ait gerekli ad alanını etkilemesini istemesini diliyorum.
underscore_d

38

Hayır, yerleşik tamsayı tiplerini yasaklamak saçma olurdu. Ancak, onlar da kötüye kullanılmamalıdır.

Tam olarak N bit genişliğinde bir tamsayıya ihtiyacınız varsa kullanın (veya bir sürüme ihtiyacınız varsa ). 32 bit tam sayı ve 64 bit tam sayı olarak düşünmek yanlıştır. Mevcut platformlarınızda böyle olabilir, ancak bu, uygulama tarafından tanımlanan davranışa dayanıyor.std::intN_tstd::uintN_tunsignedintlong long

Sabit genişlikli tam sayı türlerini kullanmak, diğer teknolojilerle birlikte çalışmak için de kullanışlıdır. Örneğin, uygulamanızın bazı kısımları Java’da ve diğerleri C ++ dilinde yazılırsa, tutarlı sonuçlar elde etmek için muhtemelen tam sayı türleriyle eşleşmek isteyeceksiniz. ( signedC ++ 'daki taşma tanımsız davranış olsa da Java'da taşma tanımının çok iyi bir anlam ifade ettiğine dikkat edin , bu nedenle tutarlılık yüksek bir hedeftir.) Farklı bilgi işlem ana bilgisayarları arasında veri alışverişinde bulunmaları da paha biçilmez olacaktır.

Tam olarak N bitlerine ihtiyacınız yoksa , ancak yeterince geniş bir türe ihtiyacınız varsa , (alan için optimize edilmiş) veya (hız için optimize edilmiş ) kullanmayı düşünün . Yine, iki ailenin de benzerleri var .std::int_leastN_tstd::int_fastN_tunsigned

Peki, yerleşik türleri ne zaman kullanmalı? Eğer Eh, standart tam genişliğini belirten olmadığından, onları kullanmak değil gerçek bit genişliğinde yaklaşık ama diğer özellikler önemsiyorum.

A char, donanım tarafından adreslenebilen en küçük tam sayıdır. Dil, sizi keyfi belleğe takma için kullanmaya zorlar. Aynı zamanda (dar) karakter dizelerini temsil eden tek uygun türdür.

Bir intgenellikle makinenin kaldırabileceği en hızlı tür olacaktır. Tek bir talimatla (bit maskelemek veya kaydırmak zorunda kalmadan) yüklenip depolanabilecek ve (en fazla) etkin donanım talimatlarıyla çalıştırılabilecek kadar dar olacak şekilde yeterince geniş olacaktır. Bu nedenle, intveri taşması ve taşma kaygı verici olmadığında aritmetik yapmak için mükemmel bir seçimdir. Örneğin, varsayılan numaralandırma türü int. Bunu sadece 32 bitlik bir tamsayı olarak değiştirmeyin, çünkü yapabilirsiniz. Ayrıca, yalnızca –1, 0 ve 1 olabilen bir değeriniz varsa,intmükemmel bir seçimdir, bunlardan büyük dizileri saklayamazsanız, bu durumda tek tek öğelere erişmek için daha yüksek bir fiyat ödemek zorunda kalmadan daha kompakt bir veri türü kullanmak isteyebilirsiniz. Daha verimli önbellekleme muhtemelen bunun karşılığını verir. Birçok işletim sistemi işlevi de açısından tanımlanmıştır int. Argümanlarını ve sonuçlarını ileri geri dönüştürmek aptalca olurdu. Muhtemelen yapabileceği tek şey bu tanıtmak taşma hataları.

longgenellikle tek bir makine talimatıyla işlenebilecek en geniş tip olacaktır. Bu özellikle unsigned longham verilerle ve her türlü bit manipülasyon işiyle uğraşmak için çok caziptir. Örneğin, unsigned longbir bit vektörünün uygulanmasında görmeyi beklerdim . Kod dikkatlice yazılmışsa, türün gerçekte ne kadar geniş olduğu önemli değildir (çünkü kod otomatik olarak adapte olur). Yerel makine sözcüğünün 32 bit olduğu platformlarda, bit vektörünün destek dizisinin bir dizi olmasıunsigned32 bit tamsayılar en çok istenir, çünkü gereksiz bitleri tekrar uzaklaştırmak ve maskelenmek için sadece pahalı talimatlar ile yüklenmesi gereken 64 bit tipini kullanmak aptalca olacaktır. Öte yandan, platformun yerel sözcük boyutu 64 bit ise, bu tür bir dizi istiyorum çünkü “ilk grubu bul” gibi işlemlerin iki kat daha hızlı çalışabileceği anlamına gelir. Dolayısıyla, longtanımladığınız veri türünün “sorunu” , boyutunun platformdan platforma değiştiği, aslında iyi kullanılabilecek bir özellik . Bu, yalnızca yerleşik türleri belli bir bit genişliğinin türü olarak düşünmüyorsanız, basitçe yapmadıkları bir problemdir.

char, intVe longyukarıda tarif edildiği gibi çok yararlı türleridir. shortve long longneredeyse yararlı değil çünkü anlambilikleri çok daha az açık.


4
OP özellikle longWindows ve Unix arasındaki büyüklük farkını dile getirdi. Yanlış anlıyor olabilirim, ancak long"sorun" yerine "özellik" olma boyutundaki farkın açıklanması, 32 ve 64 bit veri modellerinin karşılaştırılmasında anlam ifade ediyor, ancak bu karşılaştırma için değil. Bu durumda sorulan özel durumda, bu gerçekten bir özellik mi? Yoksa diğer durumlarda (genel olarak) ve bu durumda zararsız bir özellik mi?
Dan Getz

3
@ 5gon12eder: Sorun şu ki, kodun davranışının "int" boyutundan bağımsız olmasına izin vermek amacıyla uint32_t gibi türlerin yaratılması, ancak anlamı "uint32_t gibi davranacak bir türün olmaması 32- bit sistemi "davranışını doğru" int "boyutundan bağımsız olan yazma kodunu neredeyse doğru olan yazma kodundan daha zordur.
supercat,

3
Evet, biliyorum ki ... küfürün geldiği yer orasıydı. Orjinal yazarlar, kira direncini başlattılar çünkü kodu yazdıklarında, 32-bit işletim sistemleri on yıldan daha uzun bir süredir kaldılar.
Steven Burnap

8
@ 5gon12eder Ne yazık ki, supercat doğru. Tam genişlikli tiplerinin hepsi vardır "sadece Typedefs" ve tamsayı promosyon kuralları üzerinde aritmetik demektir onlardan hiçbir haber almak uint32_tdeğerler olarak yapılacaktır imzalı , intbir platform üzerinde -width aritmetik intolduğu geniş daha uint32_t. (Günümüzün ABI'leri ile bu durum son derece önemli bir konudur uint16_t.)
zwol

9
1, ayrıntılı bir cevap için teşekkürler. Ama: Ah canım. Uzun paragrafınız: " longgenellikle tek makine talimatlarıyla işlenebilecek en geniş tür olacaktır." - ve bu tam olarak yanlıştır . Windows veri modeline bakın. IMHO, aşağıdaki tüm örneğiniz bozuldu, çünkü x64'te Windows uzun hala 32 bit.
Martin Ba,

6

Başka bir cevap, cstdint türleri ve buradaki daha az bilinen varyasyonları ayrıntılı olarak açıklar.

Buna eklemek istiyorum:

etki alanına özgü tür adları kullan

Yani olmaya Parametrelerinizi ve değişkenleri bildirmek kalmamasıdır uint32_t(kesinlikle longgibi isimler!), Ancak channel_id_type, room_count_typevb

kütüphaneler hakkında

longÖzellikle bunlara referans veya işaretçi olarak kullanıldığında sinir bozucu olabilecek veya kullanamayan 3. şahıs kütüphaneleri .

En iyi şey, sarıcı yapmaktır.

Genel olarak stratejim, kullanılacak bir dizi döküm işlevi yapmak. İhtiyacınız olan herhangi bir işaretçi vs. varyasyonuyla birlikte yalnızca karşılık gelen tiplerle tam olarak eşleşen tipleri kabul etmek için aşırı yüklenirler. Os / derleyici / ayarlara özgü olarak tanımlanırlar. Bu, uyarıları kaldırmanıza ve yine de yalnızca "doğru" dönüşümlerin kullanıldığından emin olmanıza izin verir.

channel_id_type cid_out;
...
SomeLibFoo (same_thing_really<int*>(&cid_out));

Özellikle, 32 bit üreten farklı ilkel türlerle, nasıl int32_ttanımlanacağınız seçiminiz kütüphane çağrısı ile eşleşmeyebilir (örn. Windows'ta uzun).

Cast benzeri işlev , çatışmayı belgeler , işlevin parametresiyle eşleşen sonucu derleme zamanı kontrolü sağlar ve yalnızca gerçek türün dahil olan gerçek boyutla eşleşmesi durumunda herhangi bir uyarı veya hatayı kaldırır . Başka bir deyişle, int*a veya a'yı (Windows'ta) a veya a geçersem aşırı yüklenir ve tanımlanır, long*aksi takdirde derleme zamanı hatası verir.

Böylece, kitaplık güncellendiğinde veya birileri ne channel_id_typeolduğunu değiştirirse, bu doğrulanmaya devam eder.


neden aşağı oy (yorum yapmadan)?
JDługosz

Çünkü bu ağ üzerindeki en çok oy kullananlar yorumsuz gözüküyor ...
Ruslan
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.