2013-08-15 11 views
23

ja często chcąc mogłem zrobić coś takiego w C:Dlaczego C nie ma literałów binarnych?

val1 &= 0b00001111; //clear high nibble 
val2 |= 0b01000000; //set bit 7 
val3 &= ~0b00010000; //clear bit 5 

Mając tę ​​składnię wydaje się niezwykle użytecznym dodatkiem do C bez wad, że mogę myśleć, i wydaje się, że naturalną rzeczą dla język niskiego poziomu, w którym bit-twiddling jest dość powszechny.

Edycja: Widzę kilka innych wspaniałych alternatyw, ale wszystkie one rozpadać, gdy jest bardziej skomplikowana maska. Na przykład, jeśli reg jest rejestrem, który kontroluje szpilki I/O mikrokontrolera, i chcę ustawić styki 2, 3 i 7 na wysokim poziomie w tym samym czasie mógłbym napisać reg = 0x46; ale musiałem spędzić 10 sekund o tym myśleć (i prawdopodobnie będę musiał spędzić 10 sekund ponownie za każdym razem, gdy czytam ten kod po tym, jak nie patrzę na niego przez dzień lub dwa) lub mogę napisać reg = (1 << 1) | (1 << 2) | (1 << 6);, ale osobiście uważam, że jest to o wiele mniej jasne niż pisanie `reg = 0b01000110 ; " Mogę się zgodzić, że nie skaluje się to jednak znacznie poza architektury 8-bitowe lub 16-bitowe. Nie, żebym kiedykolwiek musiał zrobić maskę 32-bitową.

+1

ma hex, który moim zdaniem jest jeszcze lepszy, jeśli spędzasz 10 minut na odczuwaniu związku – vroomfondel

+0

Czy ten ostatni komentarz powinien brzmieć "clear bit 4"? (Albo jeszcze lepiej "czysty bit 3", skoro programiści powinni domyślnie policzyć od zera?) – Nemo

+0

IMHO, ponieważ jest hex. Znacznie łatwiejsze w obsłudze programisty niż liczby binarne. Jest bardzo podatny na błędy. Nie ważne jak bardzo jesteś doświadczony. –

Odpowiedz

12

To właśnie pchnął hexadecimal być ... szesnastkowo. Podstawowym zastosowaniem zapisu szesnastkowego jest "... " przyjazna dla człowieka reprezentacja wartości kodowanych binarnie w komputerach i cyfrowej elektronice ... ". byłoby to w następujący sposób:

val1 |= 0xF; 
val2 &= 0x40; 
val3 |= ~0x10; 

szesnastkowym:

  1. jeden cyfrowy heks może stanowić dziobanie (4 bity lub pół ósemkowy).
  2. Dwie cyfry szesnastkowe mogą reprezentować bajt (8 bitów).
  3. Sześciokąt jest znacznie bardziej zwarty, gdy skaluje się do większych masek.

Z pewną praktyką konwersja między szesnastkową a binarną stanie się znacznie bardziej naturalna. Spróbuj zapisywać konwersje ręcznie i nie używaj internetowego konwertera notacji bin/hex - wtedy za kilka dni stanie się to naturalne (i szybsze w rezultacie).

marginesie: Nawet literały binarne nie są standardowym C, jeśli skompilować z GCC jest możliwe użycie literałów binarnych, powinny być poprzedzone „0B” lub „0B”. Więcej informacji można znaleźć w oficjalnej dokumentacji here. Przykład:

int b1 = 0b1001; // => 9 
int b2 = 0B1001; // => 9 
+2

Tak, to jest to, co zawsze robię zamiast tego, ale zawsze muszę zrobić kilka kalkulacji w moim, aby zapamiętać, co jest binarne w hax. Zwłaszcza jeśli chcę np. usuń najniższe 6 bitów. Zgadzam się, że literały binarne będą długie na platformach 32-bitowych, ale w takim przypadku po prostu nie można z nich korzystać. – Drew

+3

Myślenie na heksie staje się drugą naturą po małej praktyce. Hex ma tę zaletę, że jest łatwiejszy do odczytania niż plik binarny. – markgz

+1

@ Drew, rozumiem twój punkt widzenia, ponieważ może być o wiele łatwiej myśleć o mniejszych maskach. Kiedy już będziesz ćwiczył wystarczająco dużo, stanie się to całkiem naturalne (jak wszystko w życiu).Zalecam ręczne wykonanie wszystkich obliczeń i podwójne sprawdzenie się przy kalkulatorze podczas tworzenia masek, abyś mógł lepiej i konwertować pomiędzy tymi dwiema notacjami. –

21

Według Rationale for International Standard - Programming Languages C §6.4.4.1 Integer stałych

Propozycja dodać stałych binarnych został odrzucony ze względu na brak precedensu i niewystarczającej użyteczności.

To nie jest w standardzie C, ale GCC obsługuje go jako rozszerzenie poprzedzony 0b lub 0B:

i = 0b101010; 

Zobacz here o szczegóły.

+0

To jest niesamowite, ale nie używam GCC :(czy wiesz, co nie jest w standardzie? – Drew

+0

@Drew Zobacz aktualizację Innymi słowy, komitet myśli, że jego użycie może być objęte stałymi heksadecymalnymi, myślę, że. –

8

Wszystkie Twoje przykłady można pisanych jeszcze wyraźniej:

val1 &= (1 << 4) - 1; //clear high nibble 
val2 |= (1 << 6); //set bit 6 
val3 &=~(1 << 3); //clear bit 3 

(Brałem wolność ustalające komentarzy liczyć od zera, jak Nature przeznaczone.)

Twój kompilator spasuje te stałe, więc nie ma kary za wydajność, aby je zapisać w ten sposób. I są one łatwiejsze do odczytania niż wersje 0b....

+0

@ Jerry Cóż, to nauczy mnie nie przestać myśleć po pierwszym błędzie. Dzięki – Nemo

+0

Nie ma problemu [wypełniacz] . –

+0

Jeśli weźmiemy pod uwagę endianię, to '(1 << 4) - 1' naprawdę jest takie samo jak' 0xF' ? ... może nie. –

5

Myślę, że czytelność jest podstawową sprawą. Chociaż na niskim poziomie, to ludzie czytają i utrzymują twój kod, a nie maszynę.

Czy łatwo jest ci się domyślić, że przez pomyłkę wpiszesz 0b1000000000000000000000000000000(0x40000000), gdzie naprawdę masz na myśli 0b10000000000000000000000000000000(0x80000000)?

+0

To wydaje się jak najlepszy powód do tej pory. Wciąż jednak nie musisz używać binarnego w tych przypadkach. I jak często tworzysz 32-bitową maskę? – Drew

+0

Ponieważ w większości przypadków mają lepszą alternatywę (szesnastkowo), myślę, że komisja właśnie zamyka drzwi do takich błędów, nie podając tego. –

+1

, ale to sprawia, że ​​trudniej zrozumieć kilka innych przypadków, na przykład, jeśli jest to rejestr, w którym bit może mieć inne znaczenie, konieczność wykupienia kalkulatora, aby sprawdzić, który bit jest włączony/wyłączony jest tylko komplikacją. Jest to typowy przypadek, gdy mamy do czynienia z czujnikami i2c/spi. – Lesto

0

Jeśli nie potrzeba rzeczywistej dosłowne, można zrobić coś takiego:

#define B_(x) strtoull(#x, 0, 2) 

unsigned char low_nibble = B_(00001111); 
unsigned char bit_7 = B_(01000000); 
unsigned char bit_5 = B_(00010000); 

val1 |= low_nibble; 
val2 &= bit_7; 
val3 |= ~bit_5; 
+2

To będzie robić dekodowanie łańcucha czasu wykonywania, trzy razy, za każdym razem, gdy ten kod jest uruchamiany. To nie przyniesie dobrego wyniku. –

+0

@DavidGiven: Wymagana wydajność oznacza, że ​​należy użyć literału. Odpowiedź już stwierdza, że ​​można tego użyć, jeśli literał nie jest wymagany. – jxh

2

„Na przykład, jeśli reg jest rejestrem, który kontroluje szpilki I/O mikrokontrolera”

Nie mogę oprzeć się wrażeniu, że to zły przykład. Bity w rejestrach kontrolnych mają określone funkcje (podobnie jak wszelkie urządzenia podłączone do poszczególnych bitów IO).

Znacznie rozsądniej byłoby podać stałe symboliczne dla wzorców bitów w pliku nagłówkowym, zamiast opracowywać kod binarny w kodzie. Konwersja binarna na szesnastkową lub ósemkową jest banalna, pamiętając, co się stanie, gdy napiszesz 01000110 do rejestru IO, a nie, szczególnie, jeśli nie masz pod ręką arkusza danych lub schematu obwodu.

W ten sposób nie tylko uratujesz te 10 sekund, próbując wypracować kod binarny, ale może nieco dłużej próbując ustalić, co robi!