3.5. Bitsel İşleçler
İşleçler C’nin önemli bir konusudur. Ancak,
hepsinin birlikte anlatılacakları tek bir yer yoktur. En iyisi gerek
duyulduğunda onları anlatmaktır. Bu bölümde, göstergelerle beraber
kullanıldıkları için, iki tekli işleç (&
ve *
) anlatılmıştı.
Bitsel işleçler başka şeylerle pek ilgisi olmayan bir grup oluşturduklarından, hepsini bu kısımda anlatmaya karar verdik. İşleçlerin azalan önceliklerine göre anlatılmaları açısından bu kısım Kısım 2.2’ye benzemektedir. Bire tümler ve atama işleçleri sağdan sola doğru, diğerleri soldan sağa doğru birleşirler. Bu kısımda anlatılan tüm işleçler sadece (çeşitli boylarda olabilen) tamsayı işlenenleri kabul ederler ve işlenenler üzerinde işlem yapmadan önce Kısım 1.6’da anlatılan tip dönüşümlerini gerçekleştirirler. Atama ile ilgili olanlar hariç, eğer işlenenler değişmez ifadeler ise, değişmez ifadeler oluştururlar. Bu işleçlerin isimleri ve kullanılan simgeler şöyledir:
İşleç | İsim |
---|---|
~ | bire tümler |
<< | sola kaydırma |
>> | sağa kaydırma |
& | bitsel VE |
^ | bitsel dışlayan VEYA |
| | bitsel VEYA |
<<= | sola kaydırma ve atama |
>>= | sağa kaydırma ve atama |
&= | bitsel VE ve atama |
^= | bitsel dışlayan VEYA ve atama |
|= | bitsel VEYA ve atama |
Detaya girmeden önce, bitsel işleçlerin mantıksal olarak ne yaptığını gösteren bir çizelge sunalım. Aşağıda y ve z tek bitlerdir:
y | z | ~ y |
y& z |
y^ z |
y| z |
0 | 0 | 1 | 0 | 0 | 0 |
0 | 1 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 0 | 1 | 1 |
1 | 1 | 0 | 1 | 0 | 1 |
Bitsel bir işleç işlenen(ler)in her bitine yukarıda anlatılan şekilde uygulanır.
Bire Tümler İşleci ~
Tekli ~
işleci işleneninin
1 olan bitlerini 0’a, 0 olan bitlerini de 1’e çevirir.
Sonucun tipi işlenenin tipiyle aynıdır.
Kaydırma İşleçleri <<
Ve >>
Bir kaydırma ifadesi
ifade_1 << ifade_2
veya
ifade_1 >> ifade_2
şeklindedir.
<<
işleci
durumunda, tip dönüşümlerinden sonra, ifade_2
int
’e çevrilir ve ilk işlenenin bitleri
ifade_2’nin değeri kadar sola kaydırılır. İşlenenin solundan
düşen
bitler kaybedilir. Sola kayan bitlerden arta kalan sağdaki
boş yerler sıfırla doldurulur. Sonucun tipi soldaki işlenenin tipidir.
>>
işleci yukarıda anlatılanların sağa doğru olan
şeklini yerine getirir, tek bir farkla: Eğer soldaki ifade
signed
(işaretli) ve olumsuz ise boş kalan yerler,
0 yerine, 1 (işaret) bitiyle doldurulabilir. Buna aritmetik
kaydırma denir ve sistemlerin çoğu bunu yapar. Eğer ilk
ifade unsigned
(işaretsiz) ise solda boşalan
bitlerin sıfırla doldurulacağı garanti edilir ve bir mantıksal kaydırma
söz konusudur.
Bitsel VE İşleci &
İkili &
işleci işlenenlerinin bitlerinin VEsini verir. Yani eğer her
iki işlenenin i’inci bitleri 1 ise, sonucun i’inci
biti de 1 olur. Aksi takdirde 0’dır. Tabii ki,
(a & b) & c
ve
a & (b & c)
aynı değerlere sahiptirler (yani
&
işlecinin birleşme özelliği
vardır). C bu gerçeği kullanır ve derleyici, daha verimli olacağı
gerekçesiyle, parantez içine alınmış olsa dahi, böyle ifadeleri (yani,
*
ve +
gibi birleşme ve değişme özelliği gösteren
işleçleri içeren ifadeleri) istediği şekilde yeniden düzenleyebilir.
Bitsel Dışlayan VEYA İşleci ^
İkili ^
işleci işlenenlerinin dışlayan VEYAsını
verir. Yani eğer işlenenlerin i’inci bitleri farklı ise,
sonucun i’inci biti 1 olur. Aksi takdirde 0’dır.
^
birleşme özelliği gösteren bir işleçtir ve bu işleci içeren
ifadeler, önceki altkısımda anlatıldığı gibi, yeniden düzenlenebilir.
Bitsel VEYA İşleci |
İkili |
işleci işlenenlerinin kapsayan VEYAsını
verir. Yani eğer her iki işlenenin i’inci bitleri 0 ise,
sonucun i’inci biti de 0 olur. Aksi takdirde 1’dir.
|
da birleşme özelliği gösteren bir işleçtir ve yeniden
düzenleme söz konusu olabilir.
Atama İşleçleri <<=
, >>=
,
&=
, ^=
Ve |=
Yukarıda anlatılan işleçler belirli ifadeler oluşturmalarına rağmen, işlenenlerinin değerlerini değiştirmezler. Şimdi anlatacağımız atama işleçleri ise işlemin sonucunu soldaki işlenenlerine aktarırlar.
Kısım 1.5’teki +=
, -=
,
*=
vs. işleçlerini anımsayın. Her bir ikili bitsel işlecin
karşılığında bir atama işleci mevcuttur,
<<
için <<=
,
>>
için >>=
,
&
için &=
,
^
için ^=
ve
|
için |=
. Bu işleçlerin işlevselliği diğer atama
işleçlerine benzer.
Bitsel İşleçler—Örnekler
short
, long
gibi tiplerin uzunlukları derleyiciden derleyiciye değiştiğine göre, buradaki
örnekler sadece bizim 64 bitlik sistemimiz için geçerlidir. Bu sistemde
short
2 bayt, int
4 bayt,
long
8 bayttır. Aşağıdaki bütün örneklerde
ifadenin değeri sekizli tabanda verilmiştir. Sonucun tipi de ayrıca
belirtilmiştir. Bütün değişkenlerin ilk değeri 5
’tir.
Değişken tanımları şöyledir:
/* signed */ char c = 5; short s = 5; int i = 5; long l = 5;
İfade | Değer | Tip |
---|---|---|
~c | 037777777772 | int |
~s | 037777777772 | int |
~i | 037777777772 | int |
~l | 01777777777777777777772 | long |
c << 3 | 050 | int |
s >> 2 | 01 | int |
i << l | 0240 | int |
c & s | 05 | int |
-i & ~l | 01777777777777777777772 | long |
c ^ 3 | 06 | int |
~s ^ 2 | 037777777770 | int |
i ^ l & i | 0 | long |
~c | 3 | 037777777773 | int |
s | s>>1 | 07 | int |
i | l | 05 | long |
c <<= s | 0240 | char |
l >>= i | 0 | long |
s &= -i | 01 | short |
i ^= l+1 | 03 | int |
c |= 16 | 025 | char |
Bir Örnek—Sayı Paketleme
Bitsel işleçlerin kullanımını göstermek için, üç sayıyı
16 bitlik kısa bir tamsayı içine paketleyen
, daha sonra ise
açan
aşağıdaki programı inceleyelim.
#include <stdio.h> #define GUN_UZ 5 #define AY_UZ 4 #define TABAN_YIL 1950 #define TAVAN_YIL (TABAN_YIL+(1<<16-GUN_UZ-AY_UZ)-1) int main (void) { unsigned const ay_ortu = ~(~0<<AY_UZ); unsigned const gun_ortu = ~(~0<<GUN_UZ); unsigned yil, ay, gun; unsigned short tarih; do { scanf("%u%u%u", &yil, &ay, &gun); } while (yil<TABAN_YIL || yil>TAVAN_YIL || ay<1 || ay>12 || gun<1 || gun>31); yil -= TABAN_YIL; tarih = (yil<<AY_UZ|ay)<<GUN_UZ | gun; printf("Paketleme tamamlandi: %ho\n", tarih); yil = ay = gun = 0; /* değişkenleritemizle*/ gun = tarih & gun_ortu; ay = (tarih>>GUN_UZ) & ay_ortu; yil = tarih>>(GUN_UZ+AY_UZ); printf("Acma tamamlandi.\nTarih: %u %u %u\n", yil+TABAN_YIL, ay, gun); return 0; } /* main */
Program başladığında 1950 ile 2077 yılları
arasında bir tarih kabul eder. (Satır 13-16.) Sadece bazı basit kontroller
yapılmıştır; 13’üncü ayın 54’üncü gününü giremezsiniz, ancak
Şubat’ın 31’ini girebilirsiniz. Sadece 1950 yılından beri geçen
yılları sakladığımız için, Satır 17’deki -=
işlemi
yapılır. Paketleme Satır 18’de yapılır: yil
içindeki geçen yıllar AY_UZ
(yani 4) bit sola kaydırılır,
ay
yeni boşalan yere VEYAlanır, sonra bunun tamamı
GUN_UZ
(5) bit sola kaydırılır ve gun
içeriye
VEYAlanır. Paketleme sonunda, tarih
in en solundaki 7 bitinde
geçen yıllar, en sağdaki 5 bitte gün ve geri kalanında ay bulunur. Bizim
sistemimizde short
bir tamsayı 16 bit kapladığına
göre, tarih
teki bütün bitler
kullanılmış olur. do
deyimindeki
erim (yayılma aralığı) kontrolleri, bu sayıların hiçbirinin kendileri için
ayrılan miktardan daha fazla bir yer isteğinde bulunmayacaklarını sağlamak
içindir.
Satır 21 açma işleminin gerçekten çalışıp çalışmadığı kuşkusunu bertaraf etmek içindir ve programın davranışında herhangi bir değişikliğe yol açmadan çıkarılabilir.
Satır 22 tarih
in gün bitlerini 1’le, geri kalanını
da 0’la VEleyerek günü alır. Buna, belli nedenden dolayı,
örtme
denir. Satır 9’da ilklenen, gun_ortu
değişkeni gerekli bit kalıbını içerir. (~0
’ın
bütün bitleri 1’dir.) Satır 23, önce ayın en sağdaki 4 bite geleceği
şekilde tarih
i kaydırır, sonra da ay dışındaki bitleri örterek
ay
ı elde eder. Satır 24’te, tarih
, sonuçta
geçen yılların elde edileceği şekilde, sağa kaydırılır. tarih
unsigned
olduğuna göre en solda hep 0 vardır.
Program yeni açılan tarihi yazarak sona erer.
Bu arada, daha ileriki bir bölümde aynı paketlemeyi yapacak başka bir yöntem göreceğiz.