10 Nisan 2021 Cumartesi

Bitsel işleçler c programlama

 BİTSEL İŞLEÇLER

Bitsel işleçler (bitwise operators), bir tamsayının bitleri üzerinde işlemler yapar. Daha çok
sistem programlarında ya da düşük seviyeli kodlarda kullanılırlar. Bitsel işleçlerin ortak
özellikleri, işleme soktukları tamsayıları bir bütün olarak değil, bit bit ele almalarıdır. Bu
işleçlerin terimleri tamsayı türlerinden olmak zorundadır. Terimlerinin gerçek sayı
türlerinden olması geçersizdir.
C dilinde toplam 11 bitsel işleç vardır. Aşağıdaki tabloda bitsel işleçler kendi aralarındaki
öncelik sırasına göre listeleniyor:

~  Bitsel değil (bitwise not)
<< Bitsel sola kaydırma (bitwise left shift)
>> Bitsel sağa kaydırma (bitwise right shift)
&  Bitsel ve (bitwise and)
^  Bitsel özel veya (bitwise exor)
|  Bitsel veya (bitwise or)
<<= Bitsel işlemli atama işleçleri
>>= Bitsel işlemli atama işleçleri
&=  Bitsel işlemli atama işleçleri
^=  Bitsel işlemli atama işleçleri
|=  Bitsel işlemli atama işleçleri

Yukarıdaki işleçler içinde, yalnızca "Bitsel değil" (bitwise not) işleci, tek terimli önek
konumunda (unary prefix) bir işleçtir. Diğerleri iki terimli (binary) araek konumunda
(infix) bulunan işleçlerdir.
Bitsel Değil İşleci
Bitsel değil işleci (bitwise not), diğer tüm tek terimli işleçler gibi işleç öncelik tablosunun
ikinci seviyesindedir.
Bu işleç, terimi olan tamsayının bitleri üzerinde 1'e tümleme işlemi yaparak bir değer elde
eder. Yani terimi olan tamsayı değerinin 1 olan bitlerini 0, 0 olan bitlerini 1 yaparak değer
üretir. İşlecin yan etkisi (side effect) yoktur. İşlecin terimi bir nesne ise, bu nesnenin
değeri değiştirilmez.
Aşağıda programı inceleyin:
#include <stdio.h>
int main()
{
unsigned int x = 0x1AC3;
unsigned int y;
y = ~x;
printf("y = %X\n", y);
return 0;
}
Yukarıdaki programın, int türünün 4 byte olduğu bir sistemde derlendiğini düşünelim. x
değişkenine atanan 0x1AC3 değeri, ikilik sayı sisteminde aşağıdaki biçimde ifade edilir:
0000 0000 0000 0000 0001 1010 1100 0011
Bu durumda y değişkenine atanan değer
1111 1111 1111 1111 1110 0101 0011 1100
olur. printf işlevi ekrana
y = FFFFE53C
yazar. Şimdi de aşağıdaki programı derleyerek çalıştırın:
#include <stdio.h>
int main()
{
int x = 0;
int y = ~x;
printf("y = %d\n", y);
return 0;
}
İşaretli ikilik sayı sisteminde, 0'ın bire tümleyeni -1 değeridir, değil mi?
Bitsel Kaydırma İşleçleri
İki tane bitsel kaydırma işleci (bitwise shift operator) vardır:
Bitsel sağa kaydırma işleci >> (bitwise right shift)
Bitsel sola kaydırma işleci << (bitwise left shift)
Her iki işleç de, öncelik tablosunun 5. seviyesinde bulunur. Dolayısıyla bu işleçlerin
önceliği tüm aritmetik işleçlerden daha düşük, fakat karşılaştırma işleçlerinden daha
yüksektir. Bitsel kaydırma işleçleri iki terimli araek konumundaki (binary infix) işleçlerdir.
Bitsel sola kaydırma işleci, soldaki terimi olan tamsayının, sağdaki terimi olan tamsayı
kadar sola kaydırılmasından elde edilen değeri üretir. Sınır dışına çıkan bitler için, sayının
sağından 0 biti ile besleme yapılır. Örnek:
#include <stdio.h>
int main()
{
unsigned int x = 52; /* x = 0000 0000 0011 0100 */
unsigned int y = 2;
unsigned int z;
z = x << y; /* z = 0000 0000 1101 0000 */
printf("z = %u\n", z); /* z = 208 */
return 0;
}
Bir tamsayıyı, sola bitsel olarak 1 kaydırmakla o tamsayının ikiyle çarpılmış değeri elde
edilir.
Bitsel sağa kaydırma işleci, soldaki terimi olan tamsayının, sağdaki terimi olan tamsayı
kadar sağa kaydırılmasından elde edilen değeri üretir. Sol terim işaretsiz (unsigned) bir
tamsayı türünden ise, ya da işaretli (signed) bir tamsayı türünden ancak pozitif değere
sahip ise, sınır dışına çıkan bitler yerine, sayının solundan besleme 0 biti ile yapılır. Sağa
kaydırılacak ifadenin işaretli bir tamsayı türünden negatif değerli olması durumunda sınır
dışına çıkan bitler için soldan yapılacak beslemenin 0 ya da 1 bitleriyle yapılması
derleyiciye bağlıdır (implementation specified). Yani derleyiciler bu durumda sayının
işaretini korumak için soldan yapılacak beslemeyi 1 biti ile yapabilecek bir kod
üretebilecekleri gibi, sayının işaretini korumayı düşünmeksizin 0 biti ile besleyecek bir
kod da üretebilirler. İşaretli negatif bir tamsayının bitsel sağa kaydırılması taşınabilir bir
özellik değildir.
Bir tamsayıyı sağa bitsel olarak 1 kaydırmakla, o sayının ikiye bölünmüş değeri elde
edilir:
#include <stdio.h>
int main()
{
unsigned int x = 52; /* x = 0000 0000 0011 0100 */
unsigned int y = 2;
unsigned int z = x >> y; /* z = 0000 0000 0000 1101 */
printf("z = %u\n", z); /* z = 13 */
return 0;
}
Bitsel kaydırma işleçlerinin yan etkileri yoktur. Yani sol terimleri bir nesne ise, bu
nesnenin bellekteki değeri değişmez. Kaydırma işlemi ile sol terim olan nesnenin değeri
değiştirilmek isteniyorsa, bu işleçlerin işlemli atama biçimleri kullanılmalıdır.
Kaydırma işleçlerinin sağ terimi, sol terimin ait olduğu türün toplam bit sayısından daha
küçük olmalıdır. Bu koşul sağlanmamış ise oluşan durum tanımsızdır (undefined
behaviour). Örneğin Windows sistemlerinde int türden bir değerin 32 ya da daha fazla
sola ya da sağa kaydırılması tanımsızdır. Bu durumdan kaçınılmalıdır.
Bitsel kaydırma işleçlerinin öncelik yönü soldan sağadır:
x << 4 >> 8
x, 2 byte'lık işaretsiz bir tamsayı değişken olsun. Yukarıdaki ifade derleyici tarafından
x << 4) >> 8
biçiminde ele alınır. Bu ifade ile x değişkeninin ortadaki 8 biti elde edilir.
Bitsel ve İşleci
"Bitsel ve" işleci (bitwise and), işleç öncelik tablosunun 8. seviyesindedir. Bu seviyenin
öncelik yönü soldan sağadır. İşlecin yan etkisi yoktur. Terimleri nesne gösteren bir ifade
ise, bu terimlerin bellekteki değeri değişmez. Değer üretmek için terimi olan tamsayıların
karşılıklı bitlerini "ve" işlemine sokar. "Ve" işlecine ilişkin işlem tablosu tablosu aşağıda
yeniden veriliyor:

x  y  x&y
1  1   1
1  0   0
0  1   0
0  0   0

"Bitsel ve" işlecinin ürettiği değer, terimlerinin karşılıklı bitlerinin "ve" işlemine
sokulmasıyla elde edilen değerdir:
#include <stdio.h>
int main()
{
unsigned int x = 0x1BC5; /* x = 0001 1011 1100 0101 */
unsigned int y = 0X3A0D; /* y = 0011 1010 0000 1101 */
unsigned int z = x & y; /* z = 0001 1010 0000 0101 */
printf("z = %X\n", z); /* z = 0x1A05 */
return 0;
}
1 biti "bitsel ve" işleminde etkisiz elemandır.
0 biti "bitsel ve" işleminde yutan elemandır.
"Mantıksal ve" işleci yerine yanlışlıkla "bitsel ve" işlecini kullanmak sık yapılan bir
hatadır. Aşağıdaki kodu dikkatli bir şekilde inceleyin:
#include <stdio.h>
int main()
{
int x = 85;
int y = 170;
if (x && y)
printf("dogru!\n");
else
printf("yanlis!\n");
if (x & y)
printf("dogru!\n");
else
printf("yanlis!\n");
return 0;
}
Yukarıdaki programda "mantıksal ve" (&&) işleci yerine yanlışlıkla "bitsel ve" (&) işleci
kullanılıyor. Hem "mantıksal ve" hem de "bitsel ve", iki terimli, araek konumunda
işleçlerdir. Derleyiciler yukarıdaki kod için bir hata ya da uyarı iletisi üretmez. Yukarıdaki
örnekte "mantıksal ve" işlecinin kullanılması durumunda, mantıksal "doğru" biçiminde
yorumlanacak olan ifade, bitsel ve işlecinin kullanılmasıyla 0 değeri üretir, mantıksal
"yanlış" olarak yorumlanır.
Bitsel Özel Veya İşleci
Bitsel "özel veya" işleci (bitwise exor) işleç öncelik tablosunun 9. seviyesindedir. Öncelik
yönü soldan sağadır. Yan etkisi yoktur, terimleri olan nesnelerin bellekteki değeri
değişmez. Değer üretmek için, terimi olan tamsayıların karşılıklı bitlerini özel veya
(exclusive or) işlemine sokar. Bitsel "özel veya" işlecine ilişkin işlem tablosu aşağıda
veriliyor:
x  y  x^y
1  1   0
1  0   1
0  1   1
0  0   0
Yukarıdaki tablo şöyle özetlenebilir: Terimlerinden ikisi de aynı değere sahip ise, üretilen
değer 0, terimlerden biri diğerinden farklı ise üretilen değer 1 olur.
#include <stdio.h>
int main()
{
unsigned int x = 0x1BC5; /* x = 0001 1011 1100 0101 */
unsigned int y = 0X3A0D; /* y = 0011 1010 0000 1101 */
unsigned int z = x ^ y; /* z = 0010 0001 1100 1000 */
printf("z = %X\n", z); /* z = 21C8 */
return 0;
}
Bir tamsayı, arka arkaya aynı değerle bitsel özel veya işlemine sokulursa, tamsayının
kendi değeri elde edilir:
#include <stdio.h>
int main()
{
unsigned int a = 0X1BC5; /* a = 0001 1011 1100 0101 */
unsigned int b = 0X3A0D; /* b = 0011 1010 0000 1101 */
a ^= b; /* a = 0010 0001 1100 1000*/
a ^= b; /* b = 0001 1011 1100 0101 */
printf("a = %X\n", a); /* a = 0X1BC5 */
return 0;
}
Bazı şifreleme algoritmalarında "özel veya" işleminin bu özelliğinden faydalanılır.
Bitsel Veya İşleci
"Bitsel veya" (bitwise or operator) işleci, işleç öncelik tablosunun 10. seviyesindedir.
Öncelik yönü soldan sağadır. Yan etkisi yoktur, terimleri nesne gösteren bir ifade ise
bellekteki değerlerini değiştirmez. Değer üretmek için terimi olan tamsayıların karşılıklı
bitlerini "veya" işlemine sokar. Bitsel veya işlecine ilişkin işlem tablosu aşağıda veriliyor:
x  y  x|y
1  1   1
1  0   1
0  1   1
0  0   0
Bitsel veya işleci, terimlerinin karşılıklı bitlerinin "veya" işlemine sokulmasıyla elde edilen
değeri üretir:
#include <stdio.h>
int main()
{
unsigned int x = 0x1BC5; /* x = 0001 1011 1100 0101 */
unsigned int y = 0X3A0D; /* y = 0011 1010 0000 1101 */
unsigned int z = x | y; /* z = 0011 1011 1100 1101 */
printf("z = %X\n", z); /* z = 0x3BCD */
return 0;
}
0 biti bitsel veya işleminde etkisiz elemandır. 1 biti bitsel ve işleminde yutan elemandır.
Aşağıdaki programda bitsel ve, özel veya, veya işleçlerinin ürettikleri değerler ikilik sayı
sisteminde ekrana yazdırılıyor. Programı derleyerek çalıştırın:
#include <stdio.h>
#include <stdlib.h>
void bit_print(int val)
{
char bits[sizeof(val) * 8 + 1];
itoa(val, bits, 2);
printf("%10d %032s\n", val, bits);
}
int main()
{
int x, y;
printf("iki sayi giriniz ");
scanf("%d%d", &x, &y);
bit_print(x);
bit_print(y);
printf("bitsel ve islemi\n");
bit_print(x & y);
printf("*********************************************************\n");
printf("bitsel ozel veya islemi\n");
bit_print(x ^ y);
printf("*********************************************************\n");
printf("bitsel veya islemi\n");
bit_print(x | y);
return 0;
}
Bitsel İşleçler Kısa Devre Davranışı Göstermez
Bitsel işleçler kısa devre davranışına sahip değidir. Yani bu işleçlerin her iki terimi de
mutlaka işlenir.
Bitsel İşlemli Atama İşleçleri
Bitsel değil işlecinin dışında, tüm bitsel işleçlere ilişkin işlemli atama işleçleri vardır. Daha
önce de söylendiği gibi bitsel işleçlerin yan etkileri (side effect) yoktur. Bitsel işleçler
terimleri olan nesnelerin bellekteki değerlerini değiştirmez. Eğer terimleri olan nesnelerin
değerlerinin değiştirilmesi isteniyorsa bu durumda işlemli atama işleçleri kullanılabilir:
x = x << y yerine x <<= y
x = x >> y yerine x >>= y
x = x & y yerine x &= y
x = x ^ y yerine x ^= y
x = x | y yerine x |= y
kullanılabilir.
Bitsel özel veya işlemli atama işleci, tamsayı türlerinden iki değişkenin değerlerinin, geçici
bir değişken olmaksızın takas (swap) edilmesinde de kullanılabilir:
#include <stdio.h>
int main()
{
int x, y;
printf("iki sayi giriniz ");
scanf("%d%d", &x, &y);
printf("x = %d\ny = %d\n", x, y);
x ^= y ^= x ^= y;
printf("x = %d\ny = %d\n", x, y);
return 0;
}
Yukarıdaki programda, x ve y değişkenlerinin değerleri bitsel özel veya işlecinin
kullanılmasıyla takas ediliyor.
Bitsel İşleçlerin Kullanılmasına İlişkin Bazı Temalar
Bitsel işleçlerin kullanılmasına daha çok sistem programlarında rastlanır. Sistem
programlarında bir tamsayının bitleri üzerinde bazı işlemler yapılması sıklıkla gerekli olur.
Aşağıda bitsel düzeyde sık yapılan işlemler açıklanıyor:
Bir Tamsayının Belirli Bir Bitinin Birlenmesi
Buna tamsayının belirli bir bitinin "set edilmesi" de denebilir. Bir tamsayının belirli bir
bitini birlemek için, söz konusu 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 örnekte bir sayının 5. biti birleniyor:
#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;
}
x bir tamsayı k da bu sayının herhangi bir bit numarası olmak üzere bir tamsayının k.
bitini birleyecek bir ifade şu biçimde yazılabilir:
x |= 1 << k
Böyle işlemleri gerçekleştirmek için kullanılan 1 << k gibi ifadelere "bitsel maske"
(bitmask) ya da yalnızca "maske" denir.
Bir Tamsayının Belirli Bir Bitinin Sıfırlanması
Bir tamsayının belirli bir bitini sıfırlamak(clear) için, söz konusu tamsayı, ilgili biti 0 olan
ve diğer bitleri 1 olan bir maskeyle "bitsel ve" işlemine sokulmalıdır. Çünkü "bitsel ve"
işleminde, 0 yutan eleman 1 ise etkisiz elemandır. Aşağıdaki örnekte bir tamsayının 5.
biti sıfırlanıyor:
#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ı, k da bu sayının herhangi bir bit numarası olmak üzere, bir sayının k. bitini
sıfırlayan bir ifade aşağıdaki gibi genelleştirilebilir:
x &= ~(1 << k);
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 oldugunun öğrenilmesi için, söz konusu
tamsayı, ilgili biti 1 olan ve diğer bitleri 0 olan bir maskeyle "bitsel ve" işlemine sokularak
mantıksal olarak yorumlanmalıdır. Çünkü "bitsel ve" işleminde 0 yutan eleman, 1 ise
etkisiz elemandır. İfade mantıksal olarak "dogru" biçiminde yorumlanırsa, ilgili bit 1,
yanlış olarak yorumlanırsa ilgili bit 0 demektir.
x bir tamsayı, k da bu sayının herhangi bir bit numarası olmak üzerebir sayının k. bitinin
1 ya da 0 olduğunu sınayan bir deyim aşağıdaki biçimde yazılabilir:
if (x & 1 << k)
/* k. bit 1 */
else
/* k. 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.
#include <stdio.h>
int main()
{
int x;
printf("bir sayi giriniz ");
scanf("%d", &x);
if (x & 1)
printf("%d tek sayi\n", x);
else
printf("%d cift sayi\n", x);
return 0;
}
Bir Tamsayının Belirli Bir Bitini Ters Çevirmek
Bazı uygulamalarda bir tamsayının belirli bir bitinin değerinin ters çevrilmesi (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 k. bitinin
değerini değiştirmek için, sayı, k. biti 1 diğer bitleri 0 olan bir maske ile "bitsel özel veya"
işlemine sokulur.
x, bir tamsayı, k da bu sayının herhangi bir bit numarası olmak üzere, bir sayının k. bitini
ters çeviren bir ifade şu şekilde yazılabilir:
x ^= 1 << k;
Birden Fazla Bit Üzerinde İşlem Yapmak
Bir tamsayının belirli bitlerini sıfırlamak için ne yapılabilir? Örneğin int türden bir
tamsayının 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 sokulabilir. Örneğin int türünün 16 bit olduğu bir sistemde
bu sayı aşağıdaki bit düzenine sahip olur:
1111 1100 0111 1111
Bu değerin onaltılık sayı sisteminde gösterimi 0xFC7F biçimindedir, değil mi?
x &= 0xFC7F;
Tamsayıların belirli bitleri üzerinde yapılan işleri, işlevlere yaptırmaya ne dersiniz?
void clearbits(int *ptr, size_t startbit, size_t nbits);
clearbits işlevi adresi gönderilen ifadenin startbit nolu bitinden başlayarak nbits tane bitini
sıfırlar.
Örneğin x isimli unsigned int türden bir değişkenin 7. 8. 9. bitlerini sıfırlamak için işlev
aşağıdaki biçimde çağrılır:
clearbits(&x, 7, 3);
void clearbits(unsigned int *ptr, size_t startbit, size_t nbits)
{
size_t k;
for (k = 0; k < nbits; ++k)
*ptr &= ~(1 << nbits + k);
}
Benzer biçimde setbits işlevi de yazılabilir. x isimli unsigned int türden bir değişkenin 7.
8. 9. bitlerini birlemek için işlev aşağıdaki biçimde çağrılır:
clearbits(&x, 7, 3);
void setbits(unsigned int *ptr, size_t startbit, size_t nbits)
{
size_t k;
for (k = 0; k < nbits; ++k)
*ptr |= ~(1 << nbits + k);
}
Peki örneğin 32 bitlik bir alan birden fazla değeri tutacak şekilde kullanılabilir mi?
Negatif sayıların kullanılmadığı düşünülürse 4 bitlik bir alanda 0 – 15 aralığındaki değerler
tutulabilir. Bir değişkenin değerinin 0 – 15 aralığında değiştiğini varsayalım: Bazı
durumlarda 4 bitle ifade edilebilen bir değerin 2 ya da 4 byte yer kaplayan bir tamsayı
türünde tutulması istenmeyebilir. 32 bitlik bir alan içinde aslında 4 ayrı değer tutulabilir,
değil mi?
Bir sayının belirli bir bit alanında bir tamsayı değerini tutmak için ne yapılabilir? Önce
sayının ilgili bitleri sıfırlanır. Bu amaç için yukarıda yazılan clearbits gibi bir işlev
çağrılabilir. Daha sonra sayı, uygun bir değerle bitsel veya işlemine sokulabilir. Aşağıdaki
işlev tanımını inceleyin:
void putvalue(unsigned int*ptr, size_t startbit, size_t nbits, int value)
{
clearbits(ptr, startbit, nbits);
*ptr |= value << startbit;
}
putvlaue işlevi adresi gönderilen nesnenin startbit nolu bitinden başlayarak nbits bitlik
alanına value değerini yerleştirir. İşlev aşağıdaki gibi çağrılabilir:
putvalue(&x, 4, 3, 7);
Bir tamsayı değişkenin belirli bit alanları içinde saklanmış değer nasıl elde edilebilir?
Şüphesiz bu iş de bir işleve yaptırılabilir:
unsigned getvalue(unsigned int x, size_t startbit, size_t nbits);
getvalue işlevi birinci parametresine aktarılan tamsayı değerin, startbit numaralı bitinden
başlayarak nbits bitlik alanında saklanan değerle geri döner:
unsigned int getvalue(unsigned int number, size_t startbit, size_t nbits)
{
number <<= sizeof(int) * 8 - startbit - nbits;
return number >>= sizeof(int) * 8 - nbits;
}
Bitsel İşleçlerin Kullanımına İlişkin Bazı Örnek Uygulamalar
Aşağıda int türden bir değeri, ekrana ikilik sayı sisteminde yazdıran showbits isimli bir
işlev tanımlanıyor:
#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 sayi giriniz : ");
scanf("%d", &val);
showbits(val);
return 0;
}
Aşağıda aynı işi değişik bir biçimde yapan showbits2 isimli bir işlevin tanımı yer alıyor:
void showbits2(int x)
{
unsigned int i = (~((unsigned)~0 >> 1));
while (i) {
putchar (x & i ? '1' : '0');
i >>= 1;
}
}
Aşağıda tamımlanan reverse_bits isimli işlev, int türden bir değerin bitlerini ters çeviriyor:
#include <stdio.h>
int reverse_bits(int number)
{
int k;
int no_of_bits = sizeof(int) * 8;
int rev_num = 0;
for (k = 0; k < no_of_bits; ++k)
if (number & 1 << k)
rev_num |= 1 << (no_of_bits - 1 - k);
return rev_num;
}
Aşağıda tamımlanan reverse isimli işlev, unsigned char türden bir değerin bitlerini ters
çeviriyor:
#include <stdio.h>
unsigned char reverse(unsigned char byte)
{
unsigned char dest;
dest = (byte << 4) | (byte >> 4);
dest = ((dest << 2) & 0xCC) | ((dest >> 2) & 0x33);
return ((dest << 1) & 0xAA) | ((dest >> 1) & 0x55);
}
Aşağıda tanımlanan no_of_setbits isimli işlev, kendisine gönderilen int türden bir değerin
kaç tane bitinin 1 olduğu bilgisi ile geri dönüyor:
#include <stdio.h>
int no_of_setbits(unsigned int value)
{
int counter = 0;
int k;
for (k = 0; k < sizeof(int) * 8; ++k)
if (value & 1<<k)
counter++;
return counter;
}
int main()
{
int number;
printf("bir sayı girin : ");
scanf("%d", &number);
printf("sayınızın %d biti 1n", no_of_setbits(number));
return 0;
}
Şimdi de çok daha hızlı çalışacak bir işlev tasarlayalım:
int no_of_setbits(unsigned int value)
{
static int bitcounts[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3,
4};
int counter = 0;
for (; value != 0; value >>= 4)
counter += bitcounts[value & 0x0F];
return counter;
}
Yukarıda tanınımlanan işlevde yer alan for döngüsü içinde, döngünün her turunda value
değişkeninin düşük anlamlı 4 bitindeki birlenmiş bitlerin sayısı, 4 bitin sayısal değerinin
bitcounts isimli diziye indis yapılmasıyla elde ediliyor. Örneğin 4 bitlik alanda ifade edilen
tamsayının değeri 11 olsun:
11 = 1011
Bu sayının toplam 3 biti 1'dir.
bitcounts dizisinin 11 indisli elemanın değeri 3'tür. Döngünün bir sonraki turuna
geçmeden önce value değişkeni sağa 4 kez kaydırılarak bu kez value'nun bir sonraki 4
bitlik alanındaki bitlere bakılıyor.
Aşağıda işlem hızını daha da artıran bir işlev tanımlanıyor:
const static char bit_array[] = {
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
};
int count1bits(unsigned long x)
{
return bit_array[x & 0xff] + bit_array[(x >> 8) & 0xff] +
bit_array[(x >> 16) & 0xff] + bit_array[(x >>24) & 0xff];
}
count1bits işlevi 32 bitlik bir tamsayının değeri 1 olan bitlerini sayıyor. İşlev bu işi
yaparken 8 bitlik bir gruptaki 1 olan bitlerin sayısını bit_array dizisini kullanarak buluyor.
Aşağıda tanımlanan gcd_b işleviyle, iki tamsayının ortak bölenlerinden en büyüğü
hesaplanıyor. İşlevin tanımı içinde kalan işlecinin kullanılmadığına, bitsel işleçlerin
kullanıldığına dikkat edin:
unsigned int gcd_b(unsigned int x, unsigned int y)
{
unsigned int temp;
unsigned int cpof = 0;
if (x == 0)
return y;
if (y == 0)
return x;
while (((x | y) & 1) == 0) {
x >>= 1;
y >>= 1;
++cpof;
}
while ((x & 1) == 0)
x >>= 1;
while (y) {
while (!(y & 1))
y >>= 1;
temp = y;
if (x > y)
y = x - y;
else
y -= x;
x = temp;
}
return x << cpof;
}


2 yorum:

  1. merhaba, bool true-false olarak tanımladığım e ve h harflerini nasıl 1-0 olarak bir dizinin içinde saklayabilirim? projemde 15 tane soru var bunlara cevap olarak e ya da h verilecek

    YanıtlaSil
    Yanıtlar
    1. Merhaba ornek calismani gonderirmisin. sifirrzero@gmail.com

      Sil

Her yorum bilgidir. Araştırmaya devam...