Soru Tek bir ucu nasıl ayarlıyorsunuz, temizliyorsunuz ve değiştiriyorsunuz?


C / C ++ 'da nasıl ayarlı, anlaşılır ve geçiş yaparsınız?


2053
2017-09-07 00:42


Menşei


Oku bunu: graphics.stanford.edu/~seander/bithacks.html ve bunu yöneteceğiniz zaman, bunu okuyun: realtimecollisiondetection.net/blog/?p=78 - ugasoft
Ayrıca kontrol etmek isteyebilirsiniz Bit Twiddler, Bit Twiddling Hacks, ve Agrega Magic Algoritmaları.
Bu bağlantı, bu işlemlerin aslında nasıl çalıştığını anlamama yardımcı oldu - cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/setBitI.html Burada daha ilginç operasyonlar bulabilirsiniz - cs.umd.edu/class/sum2003/cmsc311/Notes - rajya vardhan
Bunun, sorunun kendisi için değil, çok kullanışlı bir referans rehberinin ortaya çıkması için olduğunu hayal edebiliyorum. Her neyse, biraz bilgiye ihtiyacım olduğunda buraya geldiğimi düşünürsem. - Joey van Hummel
@glglgl / Jonathon, C ++ için etiketlenecek kadar önemlidir. Çok sayıda trafik içeren tarihi bir sorudur ve C ++ etiketi, ilgilenen programcıların bir google araması aracılığıyla bulmasına yardımcı olacaktır. - Luchian Grigore


Cevaplar:


Biraz ayarlama

Bitwise OR operatörünü kullanın (|) biraz ayarlamak.

number |= 1UL << n;

Bu ayarlayacak nbiraz number.

kullanım 1ULL Eğer number daha geniştir unsigned long; tanıtımı 1UL << n değerlendirildikten sonra gerçekleşmez 1UL << n undefined davranışının genişlikten daha fazla değişmesi long. Aynısı, örneklerin geri kalanı için de geçerlidir.

Biraz temizlenmesi

Bitsel AND operatörünü kullanın (&) biraz temizlemek için.

number &= ~(1UL << n);

Bu temizleyecektir nbiraz number. Bit dizgisini bitwise NOT işleciyle tersine çevirmelisiniz (~), sonra VEYA.

Biraz geçiş

XOR operatörü (^) biraz geçiş yapmak için kullanılabilir.

number ^= 1UL << n;

Bu geçiş yapacak nbiraz number.

Biraz kontrol

Bunu istemedin, ama ekleyebilirim.

Bir bit kontrol etmek için, n sayısını sağa kaydırın, sonra bitwise VEYA:

bit = (number >> n) & 1U;

Bu değeri nbiraz number değişkene bit.

Değiştirerek nbiraz x

ayarlamak nya biraz 1 veya 0 2's tamamlayıcı C ++ uygulamasında aşağıdaki ile elde edilebilir:

number ^= (-x ^ number) & (1UL << n);

Bit n eğer ayarlanır x olduğu 1ve temizle x olduğu 0. Eğer x Başka bir değeri var, çöp alıyorsun. x = !!x 0 veya 1'e booleanize edecektir.

Bunu 2'nin tamamlayıcı olumsuzlama davranışından bağımsız kılmak ( -1 1'in tamamlayıcısı veya işaret / büyüklük C ++ uygulamasının aksine tüm bit kümeleri vardır, imzasız olumsuzlama kullanın.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

veya

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

Taşınabilir bit manipülasyonu için işaretsiz türleri kullanmak genellikle iyi bir fikirdir.

Genel olarak kod kopyalamak / yapıştırmamak için genellikle iyi bir fikirdir ve pek çok kişi önişlemci makroları kullanır. topluluk wiki daha aşağı cevap) veya bir çeşit kapsülleme.


3003
2017-09-07 00:50



Bit set / clear (örn., AVR mikrodenetleyiciler) için yerel desteğe sahip olan platformlarda, derleyicilerin her zaman x'in her zaman 'myByte | = (1 << x)' türünü yerel bit set / clear komutlarına çevireceğini belirtmek isterim. bir sabit, örnek: (1 << 5) veya const unsigned x = 5. - Aaron
bit = sayı ve (1 << x); bit'in _Bool (<stdbool.h>) türüne sahip olmadığı sürece bit x'in değerini bit'e koymayacaktır. Aksi halde, bit = !! (sayı & (1 << x)); irade.. - Chris
neden sonuncusu değiştirmiyorsun bit = (number >> x) & 1 - aaronman
1 bir int literal, imzalandı. Yani burada tüm operasyonlar, standartlar tarafından iyi tanımlanmayan imzalı numaralar üzerinde çalışır. Standartlar, iki tamamlayıcı veya aritmetik kaydırmayı garanti etmez, bu yüzden kullanılması daha iyidir 1U. - Siyuan Ren
tercih ederim number = number & ~(1 << n) | (x << n); n-th bitini x olarak değiştirmek için. - Eliko


Standart C ++ Kitaplığı'nı Kullanma: std::bitset<N>.

Ya da artırmak versiyon: boost::dynamic_bitset.

Kendi başınızı döndürmeye gerek yok:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}

[Alpha:] > ./a.out
00010

Boost sürümü, çalışma süresi büyüklüğünde bir bitset ile karşılaştırılır. standart kütüphane derleme zamanı boyutlu bitset.


383
2017-09-18 00:34



+1. Bu std :: bitset "C" 'den kullanılabilir değil, ama yazarın sorusu "C ++" ile etiketlendiği gibi, AFAIK, cevabınız buradaki en iyisidir ... std :: vector <bool> başka bir yoldur, eğer artıları ve eksileri biliyorsa - paercebal
@andrewdotnich: vektör <bool> (maalesef) değerleri bit olarak saklayan bir uzmanlıktır. Görmek gotw.ca/publications/mill09.htm daha fazla bilgi için... - Niklas
Belki de kimse bundan söz etmedi, çünkü bu gömülü olarak etiketlendi. Çoğu gömülü sistemde STL'den vebadan hoşlanmıyorsunuz. Destek desteği, çoğu gömülü derleyicinin arasında yer alacak çok nadir bir kuştur. - Lundin
@Martin Bu çok doğru. STL ve şablonlar gibi özel performans katillerinin yanı sıra, birçok gömülü sistem bütün standart kütüphanelerden tamamen uzak durmaktadır, çünkü bunlar doğrulanması gereken bir acıdır. Gömülü dalların çoğu, statik kod analiz araçlarını gerektiren MISRA gibi standartları benimsiyor (herhangi bir yazılım profesyoneli, sadece gömülü kişileri değil, bu araçları btw kullanıyor olmalıdır). Genel olarak insanlar standart kütüphane üzerinden statik analiz yapmaktan daha iyi şeylere sahip olurlar - eğer kaynak kodları belirli bir derleyicide bile mevcutsa. - Lundin
@Lundin: İfadeleriniz aşırı derecede geniş (bu yüzden tartışmaya gerek yok). Durumların doğru olduklarını bulabileceğime eminim. Bu benim başlangıç ​​noktasını değiştirmiyor. Bu sınıfların her ikisi de gömülü sistemlerde kullanım için mükemmeldir (ve bunların kullanıldığı bir gerçeği biliyorum). Gömülü sistemlerde kullanılmayan STL / Boost ile ilgili ilk noktanız da yanlıştır. Bunları kullanmayan sistemlerin ve hatta bunları kullanan sistemlerin bile, doğru bir şekilde kullanıldığını, ancak kullanılmadıklarını söyleyen sistemlerin doğru olmadığını (çünkü sistemlerin kullanıldığı sistemler olduğundan) eminim. - Martin York


Diğer seçenek ise bit alanlarını kullanmaktır:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

3-bit bir alanı tanımlar (aslında, üç 1-bit felds). Bit işlemleri artık biraz daha basit (haha) hale geliyor:

Bir bit ayarlamak veya temizlemek için:

mybits.b = 1;
mybits.c = 0;

Bir geçiş yapmak için:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Biraz kontrol:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

Bu sadece sabit boyutlu bit alanları ile çalışır. Aksi takdirde önceki mesajlarda açıklanan bit-twiddling teknikleri başvurmak zorunda.


213
2017-09-11 00:56



Ben her zaman bitfields kullanarak buldum kötü bir fikirdir. Bitlerin tahsis edildiği (üst veya alttan) sıra üzerinde herhangi bir kontrole sahip olmamanız, bu da değeri bir seferde hariç olmak üzere sabit / taşınabilir bir şekilde serileştirmeyi imkansız kılar. DIY bit aritmetiğini bitfields ile karıştırmak da mümkün değil, örneğin bir kerede birkaç bit için test yapan bir maske yapmak. Elbette && kullanabilir ve derleyicinin doğru bir şekilde optimize edeceğini umabilirsiniz ... - R..
Bit alanları pek çok açıdan kötü, neredeyse bir kitap yazabilirim. Aslında neredeyse bunu MISRA-C uyumuna ihtiyaç duyan biraz saha programı için yapmak zorunda kaldım. MISRA-C, belgelendirilecek tüm uygulama tanımlı davranışları zorlar, bu yüzden bit alanlarında yanlış gidebilecek her şey hakkında yazı yazmayı bitirdim. Bit düzeni, endianess, dolgu bitleri, dolgu baytları, çeşitli diğer hizalama sorunları, bir bit alanından gelen ve üstü kapalı ve açık tip dönüşümler, int kullanılmadığı takdirde UB vb. Bunun yerine, daha az hata ve taşınabilir kod için bitsel operatörleri kullanın. Bit alanları tamamen yedeklidir. - Lundin
Çoğu dil özelliği gibi, bit alanları doğru şekilde kullanılabilir veya kötüye kullanılabilir. Birden fazla küçük değeri tek bir int'ye koymanız gerekiyorsa, bit alanları çok kullanışlı olabilir. Diğer taraftan, bit alanlarının gerçek içeren int ile nasıl eşleştiğine dair varsayımlar yapmaya başlarsanız, yalnızca sorun çıkarırsınız. - Ferruccio
@endolith: Bu iyi bir fikir olmaz. Çalışabilirdiniz, ancak farklı bir işlemciye veya farklı bir derleyiciye veya hatta aynı derleyicinin bir sonraki sürümüne taşınabilir. - Ferruccio
@R. Her ikisini de kullanmak mümkündür struct bir (genellikle anonim) içine konabilir union bir tamsayı ile vb çalışır. (Bunun eski bir iplik btw olduğunu anlıyorum) - Shade


Ben bit kümesini işlemek ve temizlemek için bir başlık dosyasında tanımlanan makroları kullanıyorum:

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) ((a) & (1ULL<<(b)))

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

125
2017-09-08 21:07



Bunun 5 yaşında bir mesaj olduğunu biliyorum ama bu makroların hiçbirinde argüman çoğaltması yok Dan. - Robert Kelly
BITMASK_CHECK(x,y) ((x) & (y)) olmalıdır ((x) & (y)) == (y) aksi halde multibit maskede yanlış sonuç verir (ör. 5 vs. 3) / * Tüm mezarlara merhaba:) * / - brigadir
1 olmalı (uintmax_t)1 veya bu makroları herkesin long veya daha büyük tip - M.M
Veya 1ULL gibi çalışır (uintmax_t) çoğu uygulamada. - Peter Cordes
@brigadir: herhangi bir bit kümesini veya tüm bit kümelerini kontrol etmek isteyip istemediğinize bağlıdır. Her ikisini de açıklayıcı isimlerle dahil etmek için cevabı güncelledim. - Peter Cordes


Bazen kullanmak bir enum için isim bitler:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

Sonra kullan isimler daha sonra. Yani yazmak

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

ayarlamak, temizlemek ve test etmek. Böylelikle büyü numaralarını kodunuzun geri kalanından gizlersiniz.

Bunun dışında Jeremy'nin çözümünü onaylıyorum.


99
2017-09-17 02:04



Alternatif olarak bir clearbits() yerine işlev &= ~. Bunun için neden bir enum kullanıyorsunuz? Bunların gizli keyfi değeri olan bir grup benzersiz değişken oluşturduğunu sanıyordum, ancak her birine kesin bir değer ataıyorsunuz. Öyleyse, onları sadece değişken olarak tanımlamak yararı nedir? - endolith
@endolith: kullanımı enumİlgili sabitlerin kümeleri için, c programlamasında uzun bir yol geri gider. Ben modern derleyiciler ile tek avantajı üzerinden şüpheleniyorum const short ya da her ne olursa olsun, açıkça bir araya getirilmişler. Ve onları bir şey için istediğinde diğer Otomatik numaralandırmayı elde edersiniz. C ++ 'da, elbette size, statik hata kontrolü için biraz ekstralar veren farklı türler de oluştururlar. - dmckee
Bitlerin olası değerlerinin her biri için bir sabit tanımlamazsanız, tanımlanmamış enum sabitlerine girersiniz. Bu ne enum ThingFlags için değer ThingError|ThingFlag1, Örneğin? - Luis Colorado
Bu yöntemi kullanırsanız, lütfen enum sabitlerinin her zaman imzalı olduğunu unutmayın. int. Bu, örtülü tamsayı tanıtımı veya imzalı türlerde bitsel işlemler nedeniyle tüm ince hatalara neden olabilir. thingstate = ThingFlag1 >> 1Örneğin, uygulama tanımlı davranışı çağırır. thingstate = (ThingFlag1 >> x) << y tanımlanmamış davranışları çağırır. Ve bunun gibi. Güvende olmak için, her zaman imzasız bir türe dökün. - Lundin
@Lundin: C ++ 11'den itibaren, bir sayımın altta yatan türünü ayarlayabilirsiniz, örn .: enum My16Bits: unsigned short { ... }; - Aiken Drum


itibaren snip-c.zipbitops.h:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

Tamam, bir şeyi analiz edelim ...

Bunların hepsinde sorun yaşadığınızı düşündüğünüz genel ifade "(1L << (posn))" dır. Tüm bu tek bir bit ile bir maske oluşturmak ve herhangi bir tamsayı türü ile çalışacaktır. "Posn" argümanı belirtir biti istediğin pozisyon. Eğer poz == 0 ise, o zaman bu ifade değerlendirmek için:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

Posn == 8 ise, değerlendirir

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

Başka bir deyişle, sadece belirtilen bir 1 ile 0'lık bir alan oluşturur. konumu. Tek zor kısmı, ayarlamamız gereken BitClr () makrosundadır. 1'li bir alanda tek bir 0 bit. Bu 1 kullanılarak yapılır tilde (~) operatörü tarafından belirtilen aynı ifadenin tamamlayıcısı.

Maske oluşturulduğunda, önerdiğiniz gibi argümana uygulanır. bitsel ve (&), veya (|) ve xor (^) operatörlerini kullanarak. Maske beri uzunluğunda, makrolar sadece char's, short's, int, üzerinde çalışacak ya da uzun.

En alt satır, bunun bütün bir sınıfın genel bir çözümü olması. sorunları. Tabii ki, mümkün ve hatta yeniden yazmak için uygun her defasında açık maske değerleri olan bu makrolardan herhangi birine eşdeğerdir birine ihtiyacım var, ama neden yapıyorsun? Unutmayın, makro ikamesi Önişlemci ve böylece oluşturulan kod değerlerin gerçeğini yansıtacak derleyici tarafından sabit olarak kabul edilir - yani, sadece kullanmak kadar verimli Yapmanız gereken her zaman "tekerleği yeniden icat etmek" için genelleştirilmiş makrolar bit manipülasyonu.

Ikna? İşte bazı test kodları - Tam optimizasyon ile Watcom C'yi kullandım ve _cdecl kullanmadan, sonuçta ortaya çıkan demontaj, kadar temiz olacaktır. mümkün:

---- [TEST.C] ----------------------------------------- -----------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [TEST.OUT (demonte)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

---- [finis] ------------------------------------------- ----------------------


34
2018-06-05 14:18



Bununla ilgili 2 şey: (1) makrolarınızı perdelemede, bazıları yanlış olarak makroların argde biti / temizle / dize çevirdiğine inanmaz, ancak hiçbir ödev yoktur; (2) test.c tamamlanmadı; Daha fazla dava açıp çalıştırmadığınızdan şüpheliyim (okuyucu egzersiz) - Dan
-1 Bu sadece garip bir gizleme. C dilini makroların arkasındaki dil sözdizimini gizleyerek hiçbir zaman yeniden icat etmeyin; çok Kötü uygulama Sonra bazı tuhaflıklar: ilk olarak, 1L imzalandı, yani tüm bit işlemleri imzalı bir şekilde gerçekleştirilecektir. Bu makrolara geçen her şey uzun süre imzalanmış olarak geri dönecektir. İyi değil. İkincisi, bu işlem, operasyonların int seviyesinde olabileceği uzun sürelerce, küçük CPU'larda çok etkisiz bir şekilde çalışacaktır. Üçüncü olarak, işlev benzeri makrolar tüm kötülüklerin köküdür: Hiçbir tür güvenliğiniz yoktur. Ayrıca, ödev hakkında önceki yorum çok geçerlidir. - Lundin
Bu başarısız olur arg olduğu long long. 1L mümkün olan en geniş tip olmalı (uintmax_t)1 . (Uzaklaşabilirsin 1ull) - M.M
Kod boyutu için optimize ettiniz mi? Intel ana işlemcili CPU'larda, bu işlev döndükten sonra AX veya EAX okurken kısmi kayıt tezgahları alırsınız, çünkü EAX'in 8 bitlik bileşenlerini yazar. (AMD CPU'larında veya kısmi yazmaçları tam kayıttan ayrı olarak yeniden adlandırmayan diğer kişiler için iyidir. Haswell / Skylake, AL'yi ayrı olarak yeniden adlandırmaz, ancak AH'yi yeniden adlandırır.). - Peter Cordes


Yeni başlayan için bir örnekle biraz daha açıklamak istiyorum:

Örnek:

value is 0x55;
bitnum : 3rd.

& operatör biti kontrol etmek için kullanılır:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

Toggle veya Çevir:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| operatör: biti ayarla

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

29
2017-09-07 00:45





Bitsel operatörleri kullanın: &  | 

Son bit'i ayarlamak için 000b:

foo = foo | 001b

Son bit'i kontrol etmek için foo:

if ( foo & 001b ) ....

İçindeki son biti temizlemek için foo:

foo = foo & 110b

kullandım XXXb açıklık için. Büyük olasılıkla HEX temsili ile çalışarak, bitleri doldurduğunuz veri yapısına bağlı olarak çalışacaksınız.


26
2017-07-13 06:53



C.'de ikili bir gösterim yoktur. İkili tamsayı sabitleri standart olmayan bir uzantıdır. - Lundin