Tüm bitleri true olarak ayarlamak için -1 kullanmak güvenli midir?


132

Bu kalıbın C & C ++ 'da çok kullanıldığını gördüm.

unsigned int flags = -1;  // all bits are true

Bu, bunu başarmanın iyi bir taşınabilir yolu mu? Ya da kullanıyor mu 0xffffffffyoksa ~0daha mı iyi?


1
Anlamıyorum, lütfen açıklar mısın?
corazza

8
Bence kodun anlamının açık olup olmadığı en önemli soru. Her -1zaman işe yarayacak olsa da , sonrasında bir yoruma ihtiyaç duyulması, bunun açık bir kod olmadığını gösterir. Değişkenin bir bayraklar koleksiyonu olması gerekiyorsa, neden ona bir tamsayı atasın? Türü bir tamsayı olabilir, ancak semantik olarak kesinlikle bir tamsayı değildir. Onu asla artırmayacak veya çarpmayacaksınız. Bu yüzden 0xfffffffftaşınabilirlik veya doğruluk için değil, netlik için kullanırım.
Cam Jackson

@CamJackson yorum değildir ve C kodu yazan herhangi biri değerlerin nasıl temsil edildiğine aşina olabilir.
Miles Rout

Soru başlangıçta doğru şekilde C ve C ++ olarak etiketlenmişti. Diller, C ++ 'nın ikinin tamamlayıcısını gerektirecek bir önerisi olması nedeniyle farklı olabilir. Bununla birlikte, -1her iki dil için de taşınabilir ve geriye dönük uyumlu bir çözüm olarak kaldığı gerçeğini değiştirmez , ancak diğer yanıtlardaki bazı mantığı etkileyebilir.
Adrian McCarthy

Yanıtlar:


154

En basit olanı olduğu için tam olarak gösterdiğiniz gibi yapmanızı tavsiye ederim. Gerçek işaret temsilinden bağımsız olarak her zaman-1 işe yarayacak olanı başlatın , ancak bazen şaşırtıcı davranışlara sahip olursunuz çünkü doğru işlenen türüne sahip olmanız gerekir. Ancak o zaman bir türün en yüksek değerini elde edersiniz .~unsigned

Olası bir sürpriz örneği için şunu düşünün:

unsigned long a = ~0u;

Tüm bitleri 1 olan bir kalıbı mutlaka saklamaz a. Ama önce bir içindeki tüm bitleri 1 olan bir model oluşturacak unsigned intve sonra onu atayacaktır a. Daha unsigned longfazla bit olduğunda olan şey, bunların hepsinin 1 olmamasıdır.

Ve iki olmayanın tümleyen temsilinde başarısız olacak olan şunu düşünün:

unsigned int a = ~0; // Should have done ~0u !

Bunun nedeni, ~0tüm bitleri tersine çevirmek zorunda olmasıdır. Tersine çevirme, -1ikinin tamamlayıcı makinesinde (ihtiyacımız olan değer bu!) Sonuç verecek , ancak başka bir temsilde sonuç vermeyecektir-1 . Birinin tamamlayıcı makinesinde sıfır verir. Böylece, birinin tamamlayıcı makinesinde yukarıdakiler asıfıra başlayacaktır .

Anlamanız gereken şey, her şeyin değerlerle ilgili olmasıdır - bitlerle değil. Değişken bir değer ile başlatılır . Başlatıcıda, başlatma için kullanılan değişkenin bitlerini değiştirirseniz, değer bu bitlere göre üretilecektir. aMümkün olan en yüksek değere başlamak için ihtiyacınız olan değer -1veya UINT_MAX. İkinci tipine bağlıdır a- Kullanmak gerekecektir ULONG_MAXbir için unsigned long. Ancak, ilki türüne bağlı olmayacak ve en yüksek değeri elde etmenin güzel bir yoludur.

Biz edilir değil olmadığı hakkında konuşurken -1(her zaman sahip değildir) tüm bitlerini birine sahiptir. Ve biz konum değil olmadığı hakkında konuşurken ~0bütün bitleri bir (elbette vardır) sahiptir.

Ancak bahsettiğimiz şey, başlatılmış flagsdeğişkenin sonucunun ne olduğu. Ve bunun için sadece-1 her tür ve makineyle çalışacaktır.


9
neden -1'in hepsine dönüştürülmesi garantilidir? Bu standart tarafından garanti ediliyor mu?
jalf

9
meydana gelen dönüşüm, aralık içine girene kadar art arda bir ULONG_MAX eklemesidir (C TC2 taslağında 6.3.1.3). C ++ 'da da aynıdır, sadece onu başka bir yolla biçimlendirmek (modulo 2 ^ n). Her şey matematiksel ilişkilere bağlı.
Johannes Schaub -

6
@litb: -1 çevrimini çevirmek kesinlikle maksimum işaretsiz değerleri elde etmenin güzel bir yoludur, ancak gerçekten açıklayıcı değildir; _MAX sabitlerinin var olmasının nedeni budur (C99'da SIZE_MAX eklendi); C ++ sürümü numeric_limits<size_t>::max()biraz uzun soluklu, ancak oyuncular da öyle ...
Christoph

11
"-1'in tüm bitleri bir olup olmadığından bahsetmiyoruz (her zaman sahip değildir). Ve ~ 0'ın tüm bitleri bir olup olmadığından bahsetmiyoruz (elbette vardır)." - ne ??? Ben bütün mesele düşünce oldu en nasıl bayraklar work..no Yani 1 tüm bitlerini ayarlamak için ?? Sen bakmak bit . Değer kimin umurunda?
mpen

14
@Mark soru soran umurunda. "Tüm bitleri true olarak ayarlamak için -1 kullanmak güvenli mi" diye sorar. Bu, hangi bitlerin -1temsil edildiğini sormaz, ne de bitlerin ne olduğunu sormaz ~0. Değerleri önemsemeyebiliriz, ancak derleyici umursuyor. İşlemlerin değerlerle ve değerlerle çalıştığı gerçeğini görmezden gelemeyiz. Değeri arasında ~0olmayabilir -1, ama bu size gereken bir değerdir. Cevabımı ve @ Dingo'nun özetini görün.
Johannes Schaub -

49
  • unsigned int flags = -1; taşınabilir.
  • unsigned int flags = ~0; taşınabilir değildir çünkü ikinin tamamlayıcı temsiline dayanır.
  • unsigned int flags = 0xffffffff; taşınabilir değildir çünkü 32 bitlik girişler varsayar.

Tüm bitleri C standardı tarafından garanti edilen bir şekilde ayarlamak istiyorsanız, ilkini kullanın.


10
~ 0 (yani birin tümleyen operatörü) ikinin tümleyen gösterimine nasıl güvenir?
Drew Hall

11
Bu geriye doğru var. İkili tamamlayıcı temsiline dayanan bayrakları -1'e ayarlıyor. Bir işaret + büyüklük gösteriminde eksi bir, yalnızca iki bit setine sahiptir: işaret biti ve büyüklüğün en az anlamlı biti.
Stephen C. Steel

15
C standardı, sıfırın int değerinin işaret bitine sahip olmasını ve tüm değer bitlerinin sıfır olmasını gerektirir. Birinin tamamlanmasından sonra, tüm bu parçalar birdir. Tüm bit kümeli bir int'in değerleri şöyledir: İşaret ve büyüklük: INT_MIN Bir'in tümleyicisi: -0 İkinin tümleyicisi: -1 Yani "işaretsiz int bayrakları = ~ 0;" ifadesi platformun tam sayı gösterimiyle eşleşen yukarıdaki değerden hangisi olursa olsun atayacaktır. Ancak ikinin tamamlayıcısının '-1', tüm bayrak bitlerini bire ayarlayacak tek şeydir.
Dingo

9
@Stephen: Temsil konusunda anlaştı. Ancak işaretsiz bir int'e bir int değeri atandığında, işaretsiz, int değerinin dahili temsilini benimseyerek değerini almaz (genellikle işe yarayan ikinin tamamlayıcı sistemleri hariç). İşaretsiz int'lere atanan tüm değerler modulo'dur (UINT_MAX + 1), bu nedenle -1 atamak dahili gösterim ne olursa olsun çalışır.
Dingo

20
@Mark: İki işlemi karıştırıyorsunuz. elbette tüm bitleri içeren ~0bir intdeğer verir . Ama bir atama intbir etmek unsigned intdeğil mutlaka imzalı bit deseni olarak aynı bit modeline sahip olan işaretsiz int sonuçlanabilir. Sadece 2'nin tümleyen gösterimi ile bu her zaman böyledir. 1'lerin tamamlayıcı veya işaret-büyüklüğü temsilinde, farklı bir bit modelinde sonuçlara negatif bir intdeğer atamak unsigned int. Bunun nedeni, C ++ standardının işaretli -> işaretsiz dönüşümü aynı bitlere sahip değer değil, modulo-eşit değer olarak tanımlamasıdır.
Steve Jessop

25

Açıkçası tüm fff'lerin daha okunaklı olduğunu düşünüyorum. Bunun bir antipattern olduğu yorumuna gelince, eğer tüm bitlerin ayarlanmış / temizlenmiş olmasını gerçekten umursuyorsanız, muhtemelen değişkenin boyutunu önemsediğiniz bir durumda olduğunuzu iddia ediyorum, bu da hızlanma gibi bir şey gerektirir :: uint16_t vb.


Çok fazla umursamadığınız birkaç durum vardır, ancak bunlar nadirdir. Örneğin sizeof (işaretsiz) * CHAR_BIT bitlerinin her birine bölerek N bitlik veri kümeleri üzerinde çalışan algoritmalar.
MSalters

2
+1. Veri türünün boyutu F'lerin sayısından daha büyük olsa bile (yani, tüm bitleri doğru olarak ayarlamadınız), değeri açıkça ayarladığınız için, en azından hangi bitlerin "güvenli" olduğunun farkındasınız kullanmak için "..
mpen

17

Bahsedilen problemlerden kaçınmanın bir yolu basitçe yapmaktır:

unsigned int flags = 0;
flags = ~flags;

Taşınabilir ve isabetli.


2
Ama sonra beyan yeteneğini kaybedebilir flagsolarak const.
David Stone

1
@DavidStoneunsigned int const flags = ~0u;

@Zoidberg '- Bu, ikisinin tamamlayıcısı dışındaki sistemlerde çalışmaz. Örneğin, bir kayıt büyüklüğü sistemde, ~01 olarak ayarlanmış tüm bitleri içeren bir tam sayı olduğu, ancak daha sonra bu atama zaman intiçin unsigneddeğişken flags, içinden bir değer dönüşümü gerçekleştirmek -2**31(32-bit varsayarak intiçin) (-2**31 % 2**32) == 2**31bir tam sayı olduğu, tüm bit ama 1'e ilk set ile
David Stone

Bu aynı zamanda bu cevabın tehlikeli olmasının bir nedenidir. @Zoidberg'in cevabı aynı olacak gibi görünüyor, ama aslında değil. Bununla birlikte, kodu okuyan bir kişi olarak, onu başlatmak için neden iki adım attığınızı anlamak ve muhtemelen bunu tek bir adıma değiştirmek için cazip gelmek için onu düşünmem gerekir.
David Stone

2
Ah evet, ucevabınızdaki son eki fark etmedim . Bu elbette işe yarar, ancak yine de kullandığınız veri türünü unsignediki kez ( ve daha büyük değil) belirtme sorunu vardır ve bu da hatalara yol açabilir. Yine de, atama ve ilk değişken bildirimi birbirinden daha uzaksa, hata büyük olasılıkla ortaya çıkar.
David Stone

13

C ++ 'da bayraklar için işaretsiz bir int kullanmanın iyi bir fikir olduğundan emin değilim. Bit kümesi ve benzerleri ne olacak?

std::numeric_limit<unsigned int>::max()daha iyidir çünkü 0xffffffffişaretsiz int değerinin 32 bitlik bir tamsayı olduğunu varsayar.


Bunu standardı nedeniyle seviyorum, ancak çok uzun ve türü iki kez belirtmenizi sağlıyor. 0 herhangi bir tamsayı türü olabileceğinden, ~ 0 kullanmak muhtemelen daha güvenlidir. (Çok fazla C koktuğunun farkında olmama rağmen)
Macke

Sözlü olması bir avantaj olarak görülebilir. Ama ben de ~ 0'ı seviyorum.
Edouard A.

2
Standart UINT_MAX makrosu ile kelime dağarcığını azaltabilirsiniz, çünkü yine de unsigned int türünü kodluyorsunuz.

2
@Macke C ++ 11'de türü belirtmekten kaçınabilirsiniz auto. auto const flags = std::numeric_limit<unsigned>::max().
David Stone

11
unsigned int flags = -1;  // all bits are true

"Bunu başarmanın [,] taşınabilir bir yolu bu mu?"

Taşınabilir? Evet .

İyi? Bu başlıkta gösterilen tüm karışıklıkların da gösterdiği gibi tartışmalı . Programcı arkadaşlarınızın kodu kafa karıştırmadan anlayabileceği kadar net olmak, iyi kod için ölçtüğümüz boyutlardan biri olmalıdır.

Ayrıca, bu yöntem derleyici uyarılarına eğilimlidir . Derleyicinizi sakatlamadan uyarıyı ortadan kaldırmak için, açık bir atama yapmanız gerekir. Örneğin,

unsigned int flags = static_cast<unsigned int>(-1);

Açık atama, hedef türüne dikkat etmenizi gerektirir. Hedef türüne dikkat ediyorsanız, doğal olarak diğer yaklaşımların tuzaklarından kaçınacaksınız.

Tavsiyem, hedef türüne dikkat etmek ve örtük dönüşüm olmadığından emin olmak olacaktır. Örneğin:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

Programcı arkadaşlarınız için hepsi doğru ve daha açık .

Ve C ++ 11 ile : autoBunlardan herhangi birini daha da basitleştirmek için kullanabiliriz:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

Doğruyu ve aşikarlığı sadece doğrudan daha iyi buluyorum.


10

-1'i herhangi bir işaretsiz türe dönüştürmek , standart tarafından hepsi-bir ile sonuçlanacağı garanti edilir . Bunun kullanımı ~0Ugenellikle kötüdür çünkü 0türü vardır unsigned intve açıkça böyle bir şey yazmadığınız sürece daha büyük bir işaretsiz türün tüm bitlerini doldurmaz ~0ULL. Aklı başında sistemlerde, ~0ile aynı olmalıdır -1, ancak standart tek-tamamlama ve işaret / büyüklük gösterimlerine izin verdiği için, kesinlikle taşınabilir değildir.

Elbette 0xfffffffftam olarak 32 bite ihtiyacınız olduğunu biliyorsanız yazmak her zaman uygundur , ancak birden çok türde çalışan makrolar gibi türün boyutunu bilmeseniz bile -1'in herhangi bir bağlamda çalışması avantajı vardır veya türün boyutu uygulamaya göre değişiyorsa. Hani türü yaparsanız, başka güvenli yolu sınır makroları olan tüm olanları almak için UINT_MAX, ULONG_MAX, ULLONG_MAXvb

Şahsen ben her zaman -1 kullanıyorum. Her zaman işe yarar ve bunun hakkında düşünmek zorunda değilsin.


FWIW, kullandığım “tüm 1 bitleri” kastediyorsam (tabii ~(type)0, sağ typetarafını doldurun ). Sıfır atmak yine de sıfırla sonuçlanır, bu yüzden bu açıktır ve hedef tipindeki tüm bitlerin olumsuzlanması oldukça açık bir şekilde tanımlanmıştır. Bu operasyonu gerçekten istediğim o kadar sık ​​değil; YMMV.
Donal Dostları

6
@Donal: Sadece yanılıyorsun. C, işaretsiz bir türe uymayan bir değeri dönüştürürken, değerlerin azaltılmış modulo 2 ^ n olduğunu belirtir; burada n, hedef türdeki bit sayısıdır. Bu hem işaretli değerler hem de daha büyük işaretsiz türler için geçerlidir. İkili tamamlayıcıyla hiçbir ilgisi yoktur.
R .. GitHub BUZA YARDIM ETMEYİ DUR

2
Onlar do it önemsiz olan aynı uygulamak; imzalı bir veya bir negtalimat yerine sadece işaretsiz bir çıkarma işlem kodu kullanırsınız . Sahte işaretli aritmetik davranışa sahip makinelerde ayrı işaretli / işaretsiz aritmetik işlem kodları bulunur. Elbette gerçekten iyi bir derleyici, işaretli değerler için bile her zaman işaretli işlem kodlarını yok sayar ve böylece ücretsiz olarak iki tamamlayıcı alır.
R .. GitHub BUZA YARDIM ETMEYİ DURDUR

1
@R .: var = ~(0*var)Dava, varişaretsiz tipten daha dar olduğu için başarısız olacaktır int. Belki var = ~(0U*var)? (kişisel olarak yine de tercih ediyorum -1).
caf

1
Bu cevap Johannes Schaub'ınkinden çok daha net. Bununla birlikte, dönüşüm olmadan işaretsiz bir türe negatif bir değişmez tamsayı atamak genellikle bir derleyici uyarısıyla sonuçlanır. Uyarıyı bastırmak bir cast gerektirir, bu da hedef türüne yine de dikkat etmeniz gerektiği anlamına gelir, bu nedenle UINT_MAX veya ULONG_MAX kullanabilirsiniz ve standarttaki küçük bir ayrıntıya güvenmek yerine net olabilirsiniz, çünkü birçok programcı arkadaşınızın kafasını açıkça karıştırır. .
Adrian McCarthy

5

İçerdiğiniz öğelerden #include <limits.h>birine sahip olduğunuz sürece , yalnızca kullanmalısınız

unsigned int flags = UINT_MAX;

Eğer uzun bir süre bitmek istiyorsanız, kullanabilirsiniz

unsigned long flags = ULONG_MAX;

Bu değerlerin, işaretli tamsayıların nasıl uygulandığına bakılmaksızın, sonucun tüm değer bitlerinin 1 olarak ayarlanması garanti edilir.


1
önerdiğiniz sabitler aslında limits.h'de tanımlanmıştır - stdint.h, ek tamsayı türleri için sınırları içerir (sabit boyutlu tamsayılar, intptr_t, ...)
Christoph

5

Evet. Diğer cevaplarda da belirtildiği gibi -1, en taşınabilir olanıdır; ancak, çok anlamsal değildir ve derleyici uyarılarını tetikler.

Bu sorunları çözmek için şu basit yardımcıyı deneyin:

static const struct All1s
{
    template<typename UnsignedType>
    inline operator UnsignedType(void) const
    {
        static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");
        return static_cast<UnsignedType>(-1);
    }
} ALL_BITS_TRUE;

Kullanımı:

unsigned a = ALL_BITS_TRUE;
uint8_t  b = ALL_BITS_TRUE;
uint16_t c = ALL_BITS_TRUE;
uint32_t d = ALL_BITS_TRUE;
uint64_t e = ALL_BITS_TRUE;

3
Bir C programcısı olarak, bunun gibi kodlama geceleri bana kötü rüyalar veriyor.
jforberg

Ve bunu ALL_BITS_TRUE ^ anerede aişaretli tamsayı olduğu gibi bir bağlamda kullandığınızda ne olur ? Tür, işaretli tamsayı olarak kalır ve bit modeli (nesne gösterimi), hedefin 2'nin tamamlayıcısı olup olmamasına bağlıdır.
Peter Cordes

Hayır, belirsiz ALL_BITS_TRUE ^ aolduğundan derleme hatası verir ALL_BITS_TRUE. uint32_t(ALL_BITS_TRUE) ^ aAncak gibi kullanılabilir . Kendiniz cpp.sh'de deneyebilirsiniz :) Bugünlerde kullanıcıların kullanmaya çalışmadığından emin olmak için static_assert(std::is_unsigned<UnsignedType>::value, "This is designed only for unsigned types");in ' operatore ekleyeceğim int(ALL_BITS_TRUE). Cevabı güncelleyeceğim.
Diamond Python

3

-1 şey yapmazdım. Oldukça sezgisel değil (en azından benim için). İmzalı verinin işaretsiz bir değişkene atanması, sadece şeylerin doğal düzeninin ihlali gibi görünüyor.

Senin durumunda her zaman kullanırım 0xFFFF. (Elbette değişken boyut için doğru sayıda F kullanın.)

[BTW, gerçek dünya kodunda yapılan -1 numarasının çok nadir olduğunu görüyorum.]

Eğer gerçekten bir vairable bit'lerin umurumda, ayrıca, sabit-genişliğini kullanmaya başlamak iyi bir fikir olacağını uint8_t, uint16_t, uint32_ttürleri.


2

Intel'in IA-32 işlemcilerinde, 0xFFFFFFFF'yi 64 bitlik bir kayda yazmak ve beklenen sonuçları almak sorun değil. Bunun nedeni, IA32e'nin (IA32'nin 64-bit uzantısı) yalnızca 32-bit anlıkları desteklemesidir. 64-bit komutlarda, 32-bit anındalar işaret- 64-bit'e genişletilmiştir .

Aşağıdakiler yasa dışıdır:

mov rax, 0ffffffffffffffffh

Aşağıdakiler RAX'e 64 1 koyar:

mov rax, 0ffffffffh

Tamlık için, aşağıdaki RAX'in (EAX olarak da bilinir) alt kısmına 32 1 koyar:

mov eax, 0ffffffffh

Ve aslında 0xffffffff'ı 64 bitlik bir değişkene yazmak istediğimde programlarım başarısız oldu ve bunun yerine 0xffffffffffffffff aldım. C'de bu şöyle olacaktır:

uint64_t x;
x = UINT64_C(0xffffffff)
printf("x is %"PRIx64"\n", x);

sonuç:

x is 0xffffffffffffffff

Bunu, 0xFFFFFFFF'nin 32 bit varsaydığını söyleyen tüm cevaplara bir yorum olarak göndermeyi düşündüm, ancak o kadar çok kişi cevapladı ki, bunu ayrı bir cevap olarak ekleyeceğimi düşündüm.


1
Tebrikler, bir derleyici hatası buldunuz!
tc.

Bu herhangi bir yerde bir hata olarak belgelendi mi?
Nathan Fellman

1
UINT64_C(0xffffffff)Gibi bir şeye genişleyeceğini varsayarsak 0xffffffffuLL, bu kesinlikle bir derleyici hatasıdır. C standardı değerleri büyük ölçüde tartışır , temsil edilen değer 0xffffffff4294967295'tir (36893488147419103231 değil) ve görünürde işaretli tam sayı türlerine herhangi bir dönüştürme yoktur.
tc.

2

Sorunların çok net bir açıklaması için litb'nin cevabına bakın.

Benim anlaşmazlığım, çok açık bir şekilde konuşursak, her iki durumda da hiçbir garantinin olmamasıdır. Tüm bitler kümesinde olduğu gibi 'bit sayısının gücüne ikiden küçük bir' işaretsiz değerini temsil etmeyen herhangi bir mimari bilmiyorum, ancak burada Standardın söylediği şey (3.9.1 / 7 artı Not 44):

İntegral türlerinin gösterimleri, değerleri saf bir ikili sayı sistemi kullanarak tanımlayacaktır. [Not 44:] 0 ve 1 ikili rakamlarını kullanan tamsayılar için bir konumsal gösterim, burada ardışık bitler tarafından temsil edilen değerlerin toplayıcı olduğu, 1 ile başladığı ve 2'nin ardışık integral gücü ile çarpıldığı, belki de bit hariç en yüksek konum.

Bu, bitlerden birinin herhangi bir şey olma olasılığını bırakır.


Genel olarak, dolgu bitlerinin değerinden emin olamayız. Ve eğer istersek, onlar için bir tuzak temsili oluşturabileceğimiz için tehlikede olabiliriz (ve sinyalleri yükseltebilir). Bununla birlikte, std, işaretsiz karakterin doldurma bitlerine sahip olmamasını gerektirir ve c ++ standardındaki 4.7 / 2'de, bir tamsayıyı işaretsiz bir türe dönüştürmenin, sonuçta ortaya çıkan işaretsiz değişkenin değerinin kaynak tamsayı değerine uygun en küçük değer olduğunu söyler, (modulo 2 ^ n, n == işaretsiz tipte bit sayısı). sonra (-1) == ((2 ^ n) -1) (mod 2 ^ n). 2 ^ n-1, saf bir ikili numaralandırma sisteminde ayarlanmış tüm bitlere sahiptir.
Johannes Schaub - litb

Biz ise gerçekten imzasız tip amacı temsilinde tüm bitleri 1 olmasını istiyoruz, memset gerekir. Ancak bu şekilde bir tuzak temsili oluşturabiliriz :( Her neyse, bir gerçeklemenin muhtemelen işaretsiz tam sayılarını biraz atmak için bir nedeni yoktur, bu yüzden onu değerlerini depolamak için kullanacaktır. birkaç saçma saçma bitin yorumlanması sanırım (bunlara sahip olmaması gereken karakter / imzalı karakter / işaretsiz karakter dışında). +1 tabii ki :)
Johannes Schaub - litb

Sonunda, standardın 4.7 / 2'de hangi temsili ifade ettiği konusunda daha net olabileceğini düşünüyorum. Nesne temsiline atıfta bulunuyorsa, artık bitlerin doldurulmasına yer yoktur (yanlış bir şey görmediğim bir şekilde tartışan insanlar gördüm). Ama bence değer temsilinden bahsediyor (çünkü 4.7 / 2'deki her şey zaten değerlerle ilgili - ve sonra doldurma bitleri değer bitlerinin yanına yuvalanabilir.
Johannes Schaub - litb

1
Standart, oldukça açık bir şekilde '2'nin tamamlayıcısı, 1'in tümleyicisi ve işaretli büyüklüğü' temsillerine sahip gibi görünüyor, ancak hiçbir şeyi göz ardı etmek istemiyor. Temsilleri yakalamakla ilgili de ilginç bir nokta. Anlayabildiğim kadarıyla, aktardığım bit, Standart söz konusu olduğu sürece 'saf ikili numaralama sisteminin' tanımıdır - sondaki 'hariç' biti, -1'i atmanın garantili olup olmadığı konusunda gerçekten tek şüphemdir. iş.
James Hopkin

2

Her ne kadar 0xFFFF(ya da0xFFFFFFFF , vb.) Okunması daha kolay , aksi takdirde taşınabilir olan koddaki taşınabilirliği bozabilir. Örneğin, bir veri yapısındaki kaç öğenin belirli bit setine sahip olduğunu saymak için bir kitaplık rutini düşünün (tam bitler arayan tarafından belirtilir). Rutin, bitlerin neyi temsil ettiğine dair tamamen bilinemez olabilir, ancak yine de "tüm bitler kümesi" sabitine sahip olması gerekir. Böyle bir durumda -1, herhangi bir bit boyutunda çalışacağından, onaltılık sabitten çok daha iyi olacaktır.

Diğer olasılık, typedefbit maskesi için bir değer kullanılırsa, ~ (bitMaskType) 0 kullanmak olacaktır; bit maskesi yalnızca 16 bit türü olmak olur, bu ifade sadece 16 bit ( 'int' aksi 32 bit olacaktır bile) ayarlayın ancak 16 bit gerekli olduğunu tüm olacağından, işler ince olmalıdır olacak sağlanan kimse o aslında typecast'te uygun türü kullanır.

Bu arada, formun ifadeleri, longvar &= ~[hex_constant]eğer onaltılık sabiti bir yerine sığamayacak kadar büyükse int, ancak bir unsigned int. Bir int16 bit ise, o zaman longvar &= ~0x4000;veya longvar &= ~0x10000; bir bitini silecek longvar, ancak longvar &= ~0x8000;bit 15'i ve bunun üzerindeki tüm bitleri temizleyecektir. Yerine uyan değerler int, tamamlayıcı operatörün bir türe uygulanmasını sağlar int, ancak sonuç long, üst bitleri ayarlayarak işaret genişletilmiş olur . İçin çok büyük olan değerler unsigned int, türe tamamlayıcı operatörün uygulanmasını sağlar long. Bununla birlikte, bu boyutlar arasında olan değerler, tümleme operatörünü yazıya uygulayacak ve unsigned intdaha sonra longişaret uzantısı olmadan yazıya dönüştürülecektir .


1

Pratik olarak: Evet

Teorik olarak: Hayır.

-1 = 0xFFFFFFFF (veya platformunuzda int ne büyüklükte olursa olsun) yalnızca ikinin tümleyen aritmetiğiyle doğrudur. Pratikte işe yarayacak, ancak ikinin tamamlayıcı gösterimi yerine gerçek bir işaret bitine sahip olduğunuz eski makineler (IBM ana bilgisayarları vb.) Var. Önerilen ~ 0 çözümünüz her yerde çalışmalıdır.


6
Ben de söyledim. Ama sonra yanıldığımı anladım, çünkü -1 işaretli, değer temsillerinden bağımsız olarak dönüştürme kuralları altında her zaman max_value işaretsiz olarak dönüştürülür. En azından, C ++ 'da var, elimde C standardı yok.
Steve Jessop

6
bir belirsizlik var. -1, 0xFFFFFFFF değildir. Ancak işaretsiz bir int (32 bit) dönüştürülürse -1 0xFFFFFFFF olur. Bence bu tartışmayı bu kadar zorlaştıran da bu. Pek çok insan bu küçük dizeler hakkında konuşurken akıllarında çok farklı şeyler vardır.
Johannes Schaub - litb

1

Diğerlerinin de belirttiği gibi -1, tüm bitleri 1'e ayarlanmış olarak işaretsiz bir türe dönüşecek bir tamsayı oluşturmanın doğru yoludur. Bununla birlikte, C ++ 'daki en önemli şey doğru türleri kullanmaktır. Bu nedenle, probleminizin doğru cevabı (sorduğunuz sorunun cevabını içerir) şudur:

std::bitset<32> const flags(-1);

Bu her zaman tam olarak ihtiyacınız olan bit miktarını içerecektir. std::bitsetDiğer cevaplarda belirtilen nedenlerle tüm bitleri 1'e ayarlanmış bir a oluşturur.


0

Kesinlikle güvenlidir, çünkü -1 her zaman mevcut tüm bitlere sahip olacaktır, ancak ~ 0'ı daha çok seviyorum. -1 bir için pek mantıklı değil unsigned int. 0xFF... türün genişliğine bağlı olduğu için iyi değildir.


4
"0xFF ... tipin genişliğine bağlı olduğu için iyi değil" Gidilecek tek mantıklı yol hangisi, bence. Programınızda her bir bayrak / bitin ne anlama geldiğini açıkça tanımlamanız gerekir. Dolayısıyla, bayrakları depolamak için en düşük 32 biti kullandığınızı tanımlarsanız,
int'in

0

Diyorum:

int x;
memset(&x, 0xFF, sizeof(int));

Bu her zaman size istenen sonucu verecektir.


4
9 bit karakterli sistemlerde değil!
tc.

0

İşaretsiz bir tür için tüm bitleri bire atamanın, verilen tür için mümkün olan maksimum değeri almaya
ve sorunun kapsamını tüm işaretsiz tam sayı türlerine genişletmeye eşdeğer olduğu gerçeğinden yararlanarak :

-1 atama , hem C hem de C ++ için herhangi bir işaretsiz tam sayı türü (işaretsiz int, uint8_t, uint16_t, vb.) İçin çalışır.

Alternatif olarak, C ++ için şunlardan birini yapabilirsiniz:

  1. Dahil et <limits>ve kullanstd::numeric_limits< your_type >::max()
  2. Özel bir şablonlu işlev yazın (Bu aynı zamanda bazı mantıklı kontrollere izin verir, yani hedef türü gerçekten imzasız bir türse)

Amaç, daha fazla netlik eklemek olabilir, çünkü atama -1her zaman bazı açıklayıcı yorumlara ihtiyaç duyacaktır.


0

Anlamı biraz daha açık hale getirmenin ve yine de türü tekrar etmekten kaçınmanın bir yolu:

const auto flags = static_cast<unsigned int>(-1);

-6

evet, gösterilen gösterim çok doğrudur, sanki bunu tersi yaparsak u bir operatörün tüm bitleri tersine çevirmesini gerektirecektir, ancak bu durumda, makinedeki tamsayıların boyutunu dikkate alırsak mantık oldukça basittir.

örneğin çoğu makinede bir tamsayı 2 bayt = 16 bit tutabileceği maksimum değer 2 ^ 16-1 = 65535 2 ^ 16 = 65536'dır

0% 65536 = 0 -1% 65536 = 65535 hangi 1111 ............. 1'e karşılık gelir ve tüm bitler 1'e ayarlanmıştır (eğer kalıntı sınıfları mod 65536'yı düşünürsek) bu nedenle çoktur basit.

sanırım

hayır, bu fikri göz önünde bulundurursanız, işaretsiz girişler için mükemmel bir yemek olur ve aslında işe yarar

sadece aşağıdaki program parçasını kontrol edin

int main () {

unsigned int a=2;

cout<<(unsigned int)pow(double(a),double(sizeof(a)*8));

unsigned int b=-1;

cout<<"\n"<<b;

getchar();

return 0;

}

b = 4294967295 için yanıt, 4 baytlık tam sayılarda -1% 2 ^ 32

dolayısıyla işaretsiz tamsayılar için mükemmel bir şekilde geçerlidir

herhangi bir tutarsızlık durumunda plzz raporu


9
İki yorum: Birincisi, "çoğu" makinedeki tam sayıların boyutu konusunda kesinlikle yanılıyorsunuz. İkincisi, txt çok zor 2 okunduğundan 2 ur seckrit dilinin toplamıdır. Bize sade İngilizce .
Konrad Rudolph
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.