Özellikle alt seviyeli kodlarda bir tamsayının bitleri üzerinde bazı işlemlerin yapılması (bitwise manipulation) sıklıkla gerekli olur. En sık yapılan bitsel işlemler şunlardır:
bir tamsayının belirli bir bitinin birlenmesi
Buna tamsayının belirli bir bitinin “set edilmesi” de denir. Bir tamsayının belirli bir bitini birlemek için, tamsayı ilgili biti 1 olan ve diğer bitleri 0 olan bir sayıyla “bitsel veya” işlemine sokulmalıdır. Çünkü bitsel veya işleminde 1 yutan eleman 0 ise etkisiz elemandır. Aşağıdaki koda bakalım:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <stdio.h> int main() { int ch = 0x0041; /* ch = 65 (0000 0000 0100 0001) */ int mask = 0x0020; /* mask = 32 (0000 0000 0010 0000) */ ch |= mask; /* ch = 97 (0000 0000 0110 0001) */ printf("ch = %d\n", ch); /* ch = 97 */ return 0; } |
Yukarıdaki kodda ch değişkeninin 5. biti birleniyor:
x bir tamsayı, n de bu sayının herhangi bir bitinin indeksi olmak üzere bir tamsayının n. bitini birleyecek bir ifade şu biçimde yazılabilir:
1 |
x |= 1 << k |
Bu tür bitsel işlemlerde kullanılan bitleri manipüle etmek için kullanılan
1 |
1 << k |
gibi ifadelere “bitsel maske” (bitmask) denir.
bir tamsayının belirli bir bitinin sıfırlanması
Bir tamsayının belirli bir bitini sıfırlamak (clear / reset) için tamsayı, ilgili biti 0 olan ve diğer bitleri 1 olan bir maskeyle “bitsel ve” işlemine sokulur. Çünkü “bitsel ve” işleminde 0 yutan eleman 1 ise etkisiz elemandır. Bu bitsel maske aynı biti birlemekte kullanılan maskenin bitsel değilidir. Aşağıdaki örnekte bir tamsayının 5. biti sıfırlanıyor:
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> int main() { int ch = 0x0061; /* ch = 97 (0000 0000 0110 0001) */ int mask = ~0x0020; /* mask = ~32 (1111 1111 1101 1111)*/ ch &= mask; /* ch = 65 (0000 0000 0100 0001) */ printf("ch = %d\n", ch); /* ch = 65 */ return 0; } |
x bir tamsayı, n de bu sayının herhangi bir bitinin indeksi olmak üzere, x tamsayısının n. bitini sıfırlayan bir ifade aşağıdaki gibi genelleştirilebilir:
1 |
x &= ~(1 << k); |
bir tamsayının belirli bir bitini değiştirmek
Bazı kodlarda bir tamsayının belirli bir bitinin değiştirilmesi (toggle – flip) gerekir. Yani söz konusu bit 1 ise 0, 0 ise 1 yapılmalıdır. Bu amaçla “bitsel özel veya” işleci kullanılır. Bitsel özel veya işlecinde 0 biti etkisiz elemandır.
Bir sayının n. bitinin değerini değiştirmek için, sayı, n. biti 1, diğer bitleri 0 olan bir maske ile “bitsel özel veya” işlemine sokulur. x, bir tamsayı, n de bu sayının herhangi bir bit numarası olmak üzere, x tamsayısının n. bitini ters çeviren bir ifade şu şekilde yazılabilir:
1 |
x ^= 1 << n; |
bir tamsayının belirli bir bit değerinin elde edilmesi (0 mı 1 mi)
Bir tamsayının belirli bir bitinin 0 mı 1 mi olduğunun öğrenilmesi için, söz konusu tamsayı, ilgili biti 1 olan ve diğer bitleri 0 olan bir maskeyle “bitsel ve” işlemine sokulmasından elde edilen değer mantıksal olarak yorumlanmalıdır. Çünkü “bitsel ve” işleminde 0 yutan eleman, 1 ise etkisiz elemandır. İfadenin lojik değeri “dogru” olursa, ilgili bit 1, yanlış ise ilgili bit 0 demektir. x bir tamsayı, n de bu tamsayının herhangi bir bitinin indeksi olmak üzere x tamsayısının n. bitinin 1 ya da 0 olduğunu sınayan bir deyim aşağıdaki biçimde yazılabilir:
1 2 3 4 |
if (x & (1 << n)) /* n. bit 1 */ else /* n. bit 0 */ |
Bir pozitif tamsayının tek sayı olup olmadığı “bitsel ve” işleciyle sınanabilir. Bir tamsayı tek sayı ise sayının 0. biti 1‘dir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <stdio.h> int main() { int x; printf("bir sayı giriniz "); scanf("%d", &x); if (x & 1) printf("%d tek sayı\n", x); else printf("%d çift sayı\n", x); return 0; } |
birden fazla bit üzerinde işlem yapmak
Bir tamsayının belirli bitlerini sıfırlamak için ne yapılabilir? Örneğin int türden bir değişkenin 7. , 8. ve 9. bitlerini, diğer bitlerin değerlerini değiştirmeden sıfırlamak isteyelim. Bunu gerçekleştirmek için, tamsayı 7., 8. ve 9. bitleri 0 olan diğer bitleri 1 olan bir maske ile bitsel ve işlemine sokabiliriz. Örneğin int türünün 16 bit olduğu bir sistemde bu maskenin ikilik sayı sistemindeki gösterimi aşağıdaki gibidir:
1 |
1111 1100 0111 1111 |
Bu tamsayının onaltılık sayı sisteminde gösterimi
1 |
0XF7CF |
biçimindedir, değil mi?
1 |
x &= 0xFC7F; |
Şimdi de bir tamsayının bitlerini standart çıkış akımına yazdıran showBits isimli işlevi tanımlayalım. Sayının ilk olarak en yüksek anlamlı bitini yazdırmalıyız, değil mi?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <stdio.h> void showBits(int x) { int i = sizeof(int) * 8 - 1; for (; i >= 0; --i) putchar (x >> i & 1 ? '1' : '0'); } int main() { int val; printf("bir sayı giriniz : "); scanf("%d", &val); showBits(val); return 0; } |
showBits işlevinde i değişkenine tamsayının bit sayısının bir eksiği ile ilk değer veriliyor. Örneğin 32 bitlik int türü için i değişkeni 31 değeriyle hayata başlayacak. i >> 31 ifadesini 1 değeri ile bitsel ve işlemine sokarak sayının 31. bitinin 1 mi 0 mı olduğunu öğreneceğiz.
Aşağıda aynı işi değişik farklı bir biçimde yapan showBits2 isimli işlevi tanımlıyoruz:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <stdio.h> void showBits2(int x) { unsigned int i = ~(~0u >> 1); while (i) { putchar (x & i ? '1' : '0'); i >>= 1; } putchar('\n'); } |
Bu kez maske olarak kullanılacak en yüksek anlamlı biti 1 diğer bitleri 0 olan bir tamsayı ile başladık. Döngünün her turunda maskemizi sağa bir pozisyon kaydırdık. Böylece maskemiz içinde bulunan 1 biti döngünün her turunda x‘in farklı bir bitine karşılık geldi.
Alt seviyeli kodlarda sık yapılan bir işlem bir tamsayının kaç bitinin 1 olduğunu saptamaktır. Birçok uygulamada bu işlemin çok hızlı yapılması gerekir. İşlev çağrılarının oluşturacağı ek maliyetten kaçınmak için 32 bitlik bir tamsayının kaç bitinin 1 olduğunu hesaplayan kodu bir işlevsel makro (function-like macro) tanımlıyoruz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//bitmanip.c dosyasi const char asbc[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, }; //bitmanip.h dosyası extern const char asbc[]; #define sbc(x) asbc[((x) & 0xff)] + asbc[((x >> 8) & 0xff)] + \ asbc[((x >> 16) & 0xff)] + asbc[((x >> 24) & 0xff)] |
Böylece bir byte değerini bu diziye indis yaptığımızda o byte’ın kaç bitinin 1 olduğunu bu tablodan elde edebiliyoruz. 32 bitlik bir tamsayı toplam 4 byte’a sahip olduğu için her bir byte için 1 olan bit sayısını ayrı ayrı bu tablodan bakarak buluyor ve bu değerleri topluyoruz.
Bir tamsayının düşük anlamlı byte‘ını tamsayı değeri olarak elde etmek için tamsayıyı 255 değeriyle bitsel ve (&) işlemine sokmamız gerekiyor. 32 bitlik tamsayımız x
1 |
0XA2B4FC86 |
1 2 3 4 |
x & 0xFF ---> 0x86 ---> asbc[0x86] ---->3 (x >> 8) & 0xFF ---> 0xFC ---> asbc[0xFC] ---->6 (x >> 16) & 0xFF ---> 0xB4 ---> asbc[0xB4] ---->4 (x >> 24) & 0xFF ---> 0xA2 ---> asbc[0xA2] ---->3 |
Örneğin 2‘nin 5. kuvveti için
1 2 |
32 00000000000000000000000000100000 31 00000000000000000000000000011111 |
1 |
#define isPowerOfTwo(x) ((x) && !((x) & (x - 1))) |