Ana sayfa > Programlama > C Programlama > İşlemciler

İşlemciler

► Detaylı anlatım      ► Video anlatım

İfade kavramının işlem yapan parçası olarak ifade edebileceğimiz işlemciler, ifade içinde yer alan değişken ve sabitlere işlem yaparlar. Belli gruplar altında sınıflandırabileceğimiz işlemcileri sıra ile incelemeye çalışalım:

Aritmetik işlemciler

C dilinde kullanılan 5 adet aritmetik işlemci aşağıda gösterilmektedir. İşlemciler öncelik sırasına göre sıralanmıştır:

C ilişkisel işlemcileri
İşlemci Kullanma amacı
+ Toplama
- Çıkarma
* Çarpma
/ Bölme
% Bölme işleminde kalanı verme

C'de, işlemciler arasında öncelik sırası adı verilen bir kural vardır. İşlemci önceliği kavramının matematik derslerinde gördüğümüz konudan hiçbir farkı yoktur. Buna göre, eğer bir işlem satırında birden fazla işlemci kullanırsak, C işlemcilere öncelik sırasına göre işlem yapar. Bu şekilde işlem yapılınca, programlar bizim düşündüğünüzden farklı sonuçlar verebilir. Bazı işlemlerin önceliğini normal kuralların dışında belirlemek istediğimizde, işlemi parantez içine almak yeterlidir.

% işlemcisi dışında kalan bütün aritmetik işlemcilerini temel veri türlerinin tamamı ile birlikte kullanabiliiz. % işlemcisi ise bölme işleminin kalanını verdiğinden sadece int veri türü ile kullanılabilir.

- işlemcisini hem çıkarma işlemi, hem de sayıların negatif değerlerini göstermek için kullanabiliriz.

Şimdi, bir örnekle öğrendiklerimizi incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int id1, id2, id3;

  id1 = 15 + 4 * 3;   /* Önce çarpma işlemi yapılır. */
  id2 = (15 + 4) * 3; /* Önce toplama işlemi yapılır. */

  id3 = 15 % 4;       /* Bölme işleminin kalanı elde edilir. */
  
  printf("id1 değişken değeri: %d\n", id1);
  printf("id2 değişken değeri: %d\n", id2);
  printf("id3 değişken değeri: %d", id3);
  
  return 0;
}

Yukarıdaki örnekte, program ekrana aşağıdaki değerleri yazar:

id1 değişken değeri: 27
id2 değişken değeri: 57
id3 değişken değeri: 3

Program ilk satırda, * işlemcisine öncelik verdiğinden, önce 4 ile 3 sayısını çarpar, sonra elde edilen sonucu 15 sayısı ile toplayarak 27 sayısı elde eder. İkinci satırda ise parantez kullanarak işlemci önceliğini toplama işlemine verir. Önce 15 ile 4 sayısını toplar, sonra elde edilen sonucu 3 sayısı ile çarparak 57 sayısını elde eder. Üçüncü işlem satırında ise bir bölme işleminin kalan değerini elde eder. Her üç sonucu ekrana yazar.

İlişkisel işlemciler

C dilinde kullanılan 6 adet ilişkisel işlemcinin öncelik sırası aşağıda yer almaktadır:

C ilişkisel işlemcileri
İşlemci Kullanma amacı
< Küçüktür
<= Küçüktür veya eşittir
> Büyüktür
>= Büyüktür veya eşittir
== Eşittir
!= Eşit değildir

İlişkisel işlemciler bir sabit, değişken veya ifadeden oluşan iki değeri karşılaştırdıktan sonra doğru veya yanlış bir sonuç verir. Doğru olarak verilen değer 1, yanlış olarak verilen değer ise 0'dır.

Şimdi, ilişkisel işlemcilerin kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int id1, id2, id3, id4;

  id1 = 21;
  id2 = 35;

  id3 = id1 > id2;
  id4 = id2 > id1;

  printf("id3 değişken değeri: %d\n", id3);
  printf("id4 değişken değeri: %d", id4);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

id3 değişken değeri: 0
id4 değişken değeri: 1

Program, bildirimini yaptığı id1 ve id2 adlı iki adet int değişkene birer sabit değer atar. Değişkenlere uyguladığı ilişkisel işlemci sonuçlarını id3 ve id4 adlı int değişkenlere atadıktan sonra ekrana yazar.

Mantıksal işlemciler

C'de kullanılan 3 adet mantıksal işlemci öncelik sırasına göre aşağıda gösterilmektedir:

C mantıksal işlemcileri
İşlemci Kullanma amacı
! NOT
&& AND
|| OR

Burada göreceğimiz mantıksal işlemciler de matematik derslerinden hatırlayacağımız gibi, doğru veya yanlış sonuç veren ifadeler arasında işlem yapan işlemcilerdir. Doğru ve yanlış ifade ile kastedilen sırasıyla 1 ve 0 değerleridir. Mantıksal işlemciler doğru ve/veya yanlış sonuçları birleştirip tek bir sonuç verirler. Genellikle ilişkisel işlemcilerle birlikte kullanılan mantıksal işlemciler AND, OR ve NOT ile ilgili mantıksal işlemlerin yerine getirilmesini sağlarlar. Doğru ifadeler için 1, yanlış ifadeler için 0 değeri kullanılarak oluşturulmuş mantıksal işlemci tablosu aşağıdadır:


x   y     x && y    x || y     !x

0   0        0         0        1
0   1        0         1        1
1   1        1         1        0
1   0        0         1        0

Yukarıdaki tabloda gösterilen x ve y ifadeleri, sabit, değişken veya ifadelerden oluşan iki adet değerin ilişkisel işlemcilerle yapılan işlemin sonucunda elde edilen sonuçları gösterir.

Şimdi, öğrendiklerimizi bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int id1, id2;
  id1 = 46;
  id2 = 138;

  // İlişkisel işlemci kullanımı
  printf("%d %d %d %d\n", (id1==id2), (id1>id2), (id1<id2), (id1!=id2));

  // İlişkisel ve mantıksal işlemcilerin birlikte kullanımı
  printf("%d %d\n", ((id1<id2) && (id1!=id2)), ((id1<id2) || (id1!=id2)));
  printf("%d %d", ((id1>id2) && (id1!=id2)), ((id1>id2) || (id1==id2)));

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

0 0 1 1
1 1
0 0

Program farklı değerler atadığı iki adet int değişken arasında yaptığı karşılaştırmaları ve bu karşılama sonuçları ile yaptığı mantıksal işlem sonuçlarını ekrana yazar. İlişkisel işlemciler öncelikli işlem gördüğünden birinci safha sonunda aşağıdaki değerler elde edilir. Daha sonra, devreye giren mantıksal işlemciler ile elde edilen sonuç ekrana yazılır.


printf("%d %d\n", ((id1<id2) && (id1!=id2)), ((id1<id2) || (id1!=id2)));

1 && 1, 1 || 1 // İlişkisel işlemler sonucu (İlk safha)
1 1            // Mantıksal işlemler sonucu (İkinci safha)

printf("%d %d", ((id1>id2) && (id1!=id2)), ((id1>id2) || (id1==id2)));

0 && 1, 0 || 0 // İlişkisel işlemler sonucu (İlk safha)
0 0            // Mantıksal işlemler sonucu (İkinci safha)

Programda, önce ilişkisel sonra mantıksal işlemcilerle yapılan uygulamaların detaylı şeması aşağıdadır:

Bit işlemcileri

C'de bit seviyesinde işlem yapan 4 adet özel işlemci vardır:

& AND

| OR

^ XOR

~ 1'in tamamlayanı

Bu işlemciler sadece char ve int veri türünden olan değerlerle kullanılabilir.

İşlem yapılan değerlerin karşılıklı gelen bit'leri karşılıklı işleme tabi tutulur.

& : Eğer, karşılıklı gelen her iki bit 1 ise, sonuç bit bölümüne 1 yazılarak sonuç değeri elde edilir.

| : Eğer karşılıklı gelen bit'lerden en az bir tanesi 1 ise, sonuç bit bölümüne 1, aksi takdirde 0 yazılarak sonuç değeri elde edilir.

^ : Eğer karşılıklı gelen bit'lerden her ikisi de aynı değeri taşıyorsa, sonuç bit bölümüne 0, aksi takdirde 1 yazılarak sonuç değeri elde edilir.

~ : Tek değere işlem yapar. int veya char bir değer içindeki bütün bit değerlerini tersine çevirir (1 ise 0, 0 ise 1 yapar).

Şimdi, bit işlemcilerinin kullanılmasını örnekler üzerinde incelemeye çalışalım:

Aşağıdaki örnekte, program iki adet int değer arasında bit işlemcileri ile işlem yaparak sonuçları ekrana yazar.


#include <stdio.h>

int main(void)
{
  unsigned short int usid1 = 83;  // 83  = 00000000 01010011
  unsigned short int usid2 = 165; // 165 = 00000000 10100101

  printf("& işlemi sonucu: %hu\n", (usid1 & usid2)); // 1 = 00000000 00000001
  printf("| işlemi sonucu: %hu\n", (usid1 | usid2)); // 247 = 00000000 11110111
  printf("^ işlemi sonucu: %hu\n", (usid1 ^ usid2)); // 246 = 00000000 11110110
  // -84 = 11111111 10101100 -166 = 11111111 01011010
  printf("~ işlemi sonucu: %d %d", (~usid1), (~usid2));

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

& işlemi sonucu: 1
| işlemi sonucu: 247
^ işlemi sonucu: 246
~ işlemi sonucu: -84 -166

Program ile yapılan işlemleri bit seviyesinde gösteren karşılaştırma bilgileri aşağıdaki tabloda yer almaktadır.

  01010011   83     01010011   83     01010011   83
  10100101  165     10100101  165     10100101  165
&                 |                 ^
----------------  ----------------  ----------------
  00000001    1     11110111  247     11110110  246

Bit kaydırma işlemcileri

C'de, bit kaydırma işlemcilerini kullanarak, char ve int değerlerin bit değerini sola veya sağa kaydırabiliriz. << ifadesi sola bit kaydırma ve >> ifadesi sağa bit kaydırma işlemcisini temsil eder. Bu işlemcilerin genel yapısı aşağıda gösterilmektedir:

değişken-adı << int-değer

değişken-adı >> int-değer

Sola kaydırma bit işlemcisini kullandığımızda, değişkenin ikili sayı sistemine göre yazılmış olan değerinde yer alan bitler << işlemcisinin sağında yer alan int değer kadar sola kayar. Bu durumda, en sağda boşalan bitlerin yerine 0 değeri gelir. Sağa kaydırma bit işlemcisi kullandığımızda ise, değişkenin ikili sayı sistemine göre yazılmış olan değerinde yer alan bitler >> işlemcisinin sağında yer alan int değer kadar sağa kayar. Bu durumda en solda boşalan bitlerin yerine 0 değeri gelir.

Bu özellikleri bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

void IntToBin(char *varname, short int val)
{
  unsigned short usimask; // 1000000000000000 (16 bit -> 32768)

  printf("%s değişken değeri: %hd ", varname, val);

  for (usimask=32768; usimask>0; usimask/=2) {
       printf("%c", ((val & usimask) ? '1' : '0'));
  }
  printf("\n");
}

int main(void)
{
  short int sid1 = 17159; // 1011100000110111
  short int sid2 = 28624; // 0110111111010000

  IntToBin("sid1", sid1);
  IntToBin("sid2", sid2);

  printf("\n");

  sid1 = sid1 << 3; // Sayının bit değerlerini 3 defa sola kaydırır.
  sid2 = sid2 >> 3; // Sayının bit değerlerini 3 defa sağa kaydırır.

  IntToBin("sid1", sid1);
  IntToBin("sid2", sid2);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

sid1 değişken değeri: 17159 0100001100000111
sid2 değişken değeri: 28624 0110111111010000

sid1 değişken değeri: 6200 0001100000111000
sid2 değişken değeri: 3578 0000110111111010

Program, unsigned short int veri türünden tanımladığı sid1 ve sid2 değişkenlerine atadığı değerleri onluk ve ikili sayı sistemine göre ekrana yazar. sid1 değişkeninin bitlerini üç defa sola, sid2 değişkeninin bitlerini ise üç defa sağa kaydırdıktan sonra, değişken değerlerini tekrar onluk ve ikili sayı sistemine göre ekrana yazar. İlk kaydırma işleminde, sid1 değişkeninin en sağında yer alan bitlerin yerine ve ikinci kaydırma işleminde sid2 değişkeninin en solunda yer alan bitlerin yerine 0 değeri gelecektir.

Bit ve bit kaydırma işlemcilerinin birlikte kullanımı

Bit ve bit kaydırma işlemcilerinin birlikte kullanımını unsigned integer bir sayının bit değerlerini değiştirme, tersine çevirme ve değerlerini alma işlemlerini yapan bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>
#include <stdlib.h>

// Bit işlem fonksiyonu mode: 1 (1 değeri verme), 0 (0 değeri verme), 2 (0 ve 1 değeri arasında geçiş yapma
long int bg_bitopr(long int val, int index, int mode);
// Belirli bir sıradaki bit değerini kontrol etme
int bg_bitcheck(long int val, int index);
// Unsigned long int değeri ikili sistemde yazma
void bg_IntToBin(char *varname, long int val);

int main(void)
{
  long int lid1 = 121;           // 00000000 00000000 00000000 01111001
  long int lid2;
  bg_IntToBin("lid1", lid1);

  // 1 değeri verme (Sağ taraftan başlayarak 10.sıradaki bit)
  lid2 = bg_bitopr(lid1, 10, 1); // 00000000 00000000 00000010 01111001 (633)
  bg_IntToBin("lid2", lid2);

  // 0 değeri verme (Sağ taraftan başlayarak 7.sıradaki bit)
  lid2 = bg_bitopr(lid2, 7, 0);  // 00000000 00000000 00000010 00111001 (569)
  bg_IntToBin("lid2", lid2);

  // Bit değerini tersine çevirme (Sağ taraftan başlayarak 12.sıradaki bit)
  lid2 = bg_bitopr(lid2, 12, 2); // 00000000 00000000 00001010 00111001 (2617)
  bg_IntToBin("lid2", lid2);

  // Bit değer kontrolu
  printf("Bit değeri: %d", (bg_bitcheck(lid2, 4) ? 1 : 0));

  return 0;
}

// Bit işlem fonksiyonu mode: 0 (0 değeri verme), 1 (1 değeri verme), 2 (0 ve 1 değeri arasında geçiş yapma)
long int bg_bitopr(long int val, int index, int mode)
{
  switch(mode) {
     case 0: // 0 değeri verme
          val = val & ~(1 << (index-1));
          break;
     case 1: // 1 değeri verme
          val = val | ( 1 << (index-1));
          break;
     case 2: // 0 ve 1 değeri arasında geçiş yapma
          val = val ^ (1 << (index-1));
          break;
  }

  return val;
}

// Belirli bir sıradaki bit değerini kontrol etme
int bg_bitcheck(long int val, int index)
{
  return ((val >> (index-1)) & 1);
}

void bg_IntToBin(char *varname, long int val)
{
  int bitsayi = sizeof(val) * 8; // long int değerin bit adet değeri
  char *cdizi = (char*) malloc(bitsayi+1); // Dizi sonu '\0' karakteri için
  int id;

  cdizi[bitsayi] = '\0';

  unsigned long int u = *(unsigned long int*)&val;

  unsigned long int mask = 1 << (bitsayi-1); // 10000000 00000000 00000000 00000000 (2.147.483.648)

  printf("%s değişken değeri: %ld ", varname, val);

  for (id=0; id<bitsayi; id++, mask >>= 1) {
       // Döngü değişkeninin her artışında mask değerinin en solundaki 1 değeri bir sağa kayar.
       cdizi[id] = (u & mask) ? '1' : '0';
  }

  printf("%s\n", cdizi);
  
  free(cdizi);
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

lid1 değişken değeri: 121 00000000000000000000000001111001
lid2 değişken değeri: 633 00000000000000000000001001111001
lid2 değişken değeri: 569 00000000000000000000001000111001
lid2 değişken değeri: 2617 00000000000000000000101000111001
Bit değeri: 1

Program, bg_bitopr() fonksiyonunu 3 farklı mod ile kullanarak, unsigned integer bir sayının bit değerlerini değiştirme, tersine çevirme ve değerlerini alma işlemlerini gerçekleştirir. Fonksiyonun index parametresi işlem yapılacak bit'in sağ taraftan başlamak üzere sırasını gösterir. Fonksiyonun mode parametresi ise, 1 değeri aldığında bit'e 1 değerinin, 0 değeri aldığında bit'e 0 değerinin verileceğini, 2 değeri aldığında ise, bit değerinin tersine çevrileceğini gösterir. Herhangi bir sıradaki bit değerinin 0 veya 1 değeri aldığı bg_bitcheck() fonksiyonu ile kontrol edilir.

İşaretçi işlemcileri

C dilinde, işaretçilerle kullanılan 3 adet işlemci vardır. Bu işlemciler aşağıda yer almaktadır:

* işlemcisi

& işlemcisi

-> işlemcisi

İşaretçiler değişkenlerin bellek adreslerini içeren değişkenler olup adresini içereceği değişkenin veri türünden tanımlı olmalıdır.

İşaretçileri kullanarak, işaretçilerin adresini içerdiği değişkenlere dolaylı yoldan işlem yapabiliriz.

İşaretçi değişkenleri, tıpkı diğer değişkenler gibi, kullanılmadan önce mutlaka tanımlanmalıdır. * işlemcisi işaretçilerin tanımlanması işleminde kullanılır. Aşağıdaki işlem satırı işaretçi tanımlamasının genel yapısını göstermektedir:

veri-türü *işaretçi-adı;

Yukarıdaki satırda yer alan veri-türü ifadesi C'de kullanılan herhangi bir veri türünü, işaretçi-adı ifadesi ise işaretçi adı olarak kullanabileceğimiz herhangi bir karakter dizisini gösterir.

Aşağıdaki işlem satırı ip adlı int veri türünden bir işaretçi tanımlar:


int *ip;

* işlemcisini ayrıca, işaretçilerin adreslerini içerdiği değişkenlerin değerlerine dolaylı yoldan işlem yapmak için kullanabiliriz.

Bir işaretçi tanımlaması yaptıktan sonra, işaretçi kullanabilmemiz için aynı veri türünden bir değişkenin adresini işaretçiye atamamız gerekir. Bu işlem için & işlemcisini kullanmamız gerekir. Aşağıdaki işlem satırı işaretçilere bir değişken adresi atama işleminin genel yapısını göstermektedir:

işaretçi-adı = &değişken-adı;

Yukarıdaki satırda yer alan işaretçi-adı ifadesi işaretçi adı olarak kullanabileceğimiz herhangi bir karakter dizisini, değişken-adı ifadesi ise herhangi bir değişken adını göstermektedir. Aşağıdaki ilk işlem satırı ip adlı int bir işaretçi ve id adlı int bir değişken tanımlar. İkinci işlem satırı ise id değişkeninin bellek adresini ip işaretçisine atar:


int *ip, id;
ip = &id;

Şimdi, işaretçi işlemcilerinin kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int *ip, id = 21;

  ip = &id; // Değişken adresini işaretçiye atama
  printf("id değişken değeri (değişken yoluyla): %d\n", id);
  printf("id değişken değeri (işaretçi yoluyla): %d\n", *ip);
  printf("id değişkeni bellek adresi (değişken yoluyla): %p\n", &id);
  printf("id değişkeni bellek adresi (işaretçi yoluyla): %p\n\n", ip);

  *ip = 35; // id değişken değerini değiştirme (işaretçi yoluyla)
  printf("id değişken değeri (değişken yoluyla): %d\n", id);
  printf("id değişken değeri (işaretçi yoluyla): %d\n", *ip);
  printf("id değişkeni bellek adresi (değişken yoluyla): %p\n", &id);
  printf("id değişkeni bellek adresi (işaretçi yoluyla): %p\n\n", ip);

  (*ip)++;  // id değişken değerini değiştirme (işaretçi yoluyla)
  printf("id değişken değeri (değişken yoluyla): %d\n", id);
  printf("id değişken değeri (işaretçi yoluyla): %d\n", *ip);
  printf("id değişkeni bellek adresi (değişken yoluyla): %p\n", &id);
  printf("id değişkeni bellek adresi (işaretçi yoluyla): %p", ip);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

id değişken değeri (değişken yoluyla): 21
id değişken değeri (işaretçi yoluyla): 21
id değişkeni bellek adresi (değişken yoluyla): 0061fef8
id değişkeni bellek adresi (işaretçi yoluyla): 0061fef8

id değişken değeri (değişken yoluyla): 35
id değişken değeri (işaretçi yoluyla): 35
id değişkeni bellek adresi (değişken yoluyla): 0061fef8
id değişkeni bellek adresi (işaretçi yoluyla): 0061fef8

id değişken değeri (değişken yoluyla): 36
id değişken değeri (işaretçi yoluyla): 36
id değişkeni bellek adresi (değişken yoluyla): 0061fef8
id değişkeni bellek adresi (işaretçi yoluyla): 0061fef8

Program, önce ip adlı int bir işaretçi ve, 21 değerini ilk değer olarak vererek, id adlı int bir değişken tanımlar. id değişken adresini ip işaretçisine atar. id değişken değerini, değişken adını kullanarak doğrudan ve işaretçi adını * işlemcisi ile kullanarak dolaylı olarak ekrana yazar. Sonra, id değişken adresini, değişken adını & işlemcisi ile kullanarak ve işaretçi adını kullanarak ekrana yazar. Aynı işlemleri, id değişken değerine işaretçi yoluyla 35 olarak değiştirdikten ve bir değer artırdıktan sonra, tekrarlar. Ekrana yazılan bellek adres değerleri her bilgisayarda farklı olacaktır.

C'de, -> işlemcisi yapılarla birlikte kullanılmaktadır. Farklı veri türlerinin tek bir isim altında toplandığı yapılar için bir işaretçi tanımlandığı zaman, bu yapı işaretçileri yoluyla, yapı değişken adresleri işaretçiye atandıktan sonra, yapının her bir elemanına ayrı ayrı ulaşabiliriz. İşaretçi yoluyla yapı elemanlarına erşim sağlamak için -> işlemcisi kullanılır.

Şimdi, işaretçi işlemcilerini yapılar ile kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  int id;
  char cd;
};

int main(void)
{
  struct yap *yp, yd1, yd2;

  yp = &yd1;
  yp->id = 21;
  yp->cd = 'A';

  yp = &yd2;
  yp->id = 35;
  yp->cd = 'B';

  printf("yd1 yapı değişkeni id değeri: %d\n", yd1.id);
  printf("yd1 yapı değişkeni cd değeri: %c\n\n", yd1.cd);

  printf("yd2 yapı değişkeni id değeri: %d\n", yd2.id);
  printf("yd2 yapı değişkeni cd değeri: %c", yd2.cd);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

yd1 yapı değişkeni id değeri: 21
yd1 yapı değişkeni cd değeri: A

yd2 yapı değişkeni id değeri: 35
yd2 yapı değişkeni cd değeri: B

Program, önce bir adet int ve bir adet char değişken içeren yap adlı bir yapı tanımlar. Sonra, bu yapı türünden yp adlı bir işaretçi ile yd1 ve yd2 adlı iki adet değişken bildirimi yapar. yd1 değişkeninin bellek adresini yp işaretçisine atar. İşaretçi yoluyla yd1 yapı kopyasındaki id değişkenine 21 sayısını ve cd değişkenine 'A' karakterini atar. yd2 değişkeninin bellek adresini yp işaretçisine atar. İşaretçi yoluyla yd2 yapı kopyasındaki id değişkenine 35 sayısını ve cd değişkenine 'B' karakterini atar. Son olarak, yapı değişkenleri yoluyla tüm değerleri ekrana yazar.

. işlemcisi

. işlemcisi de tıpkı -> işlemcisi gibi yapı ve sınıflarla birlikte kullanılır. Bir yapı veya sınıf için bir değişken tanımladığımızda . işlemcisini, bir işaretçi tanımladığımızda ise, aynı yapı veya sınıf için tanımlanan değişken bellek adresini işaretçiye atadıktan sonra, -> işlemcisini, kullanmamız gerekir. Bu işlemci ile yapının veya sınıfın her bir elemanına ayrı ayrı ulaşabiliriz.

Şimdi, . işlemcisinin yapılar ile kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  int id;
  char cd;
};

int main(void)
{
  struct yap yd1, yd2;

  yd1.id = 165;
  yd1.cd = 'K';

  yd2.id = 384;
  yd2.cd = 'T';

  printf("yd1 yapı değişkeni id değeri: %d\n", yd1.id);
  printf("yd1 yapı değişkeni cd değeri: %c\n\n", yd1.cd);

  printf("yd2 yapı değişkeni id değeri: %d\n", yd2.id);
  printf("yd2 yapı değişkeni cd değeri: %c", yd2.cd);  

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

yd1 yapı değişkeni id değeri: 165
yd1 yapı değişkeni cd değeri: K

yd2 yapı değişkeni id değeri: 384
yd2 yapı değişkeni cd değeri: T

Program, önce bir adet int ve bir adet char değişken içeren yap adlı bir yapı tanımlar. Sonra, bu yapı türünden yd1 ve yd2 adlı iki adet değişken bildirimi yapar. yd1 değişkeni yoluyla, yd1 yapı kopyasındaki id değişkenine 165 sayısını ve cd değişkenine 'K' karakterini atar. yd2 değişkeni yoluyla, yd2 yapı kopyasındaki id değişkenine 384 sayısını ve cd değişkenine 'T' karakterini atar. Son olarak, yapı değişkenleri yoluyla tüm değerleri ekrana yazar.

Artırma ve azaltma işlemcileri

Aşağıdaki işlem satırı değişken-adı ifadesi ile gösterilen değişkenin değerini bir sayı artırır:

değişken-adı = değişken-adı + 1;

C dilinde, yukarıdaki işlem satırında yer alan ifade ile aynı işlemi gerçekleştiren aşağıdaki ifadeyi kullanabiliriz:

değişken-adı++;

Bir değişken değerini birer birer artıran ve iki adet artı (+) işaretinden oluşan bu işlemciye artırma işlemcisi adı verilir. Artı işaretlerinin arasında boşluk olmamalıdır. Aşağıda yer alan her iki işlem satırı da aynı işlemi gerçekleştirir:


id = id + 1;
id++;

Yukarıdaki iki satır arasında, yaptıkları işlem açısından, hiçbir fark yoktur. Şimdi, bir örnek üzerinde konuyu incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int id;

  id = 21;
  printf("id değişken değeri: %d\n", id);

  id = id + 1;
  printf("id değişken değeri: %d\n", id);

  id++;
  printf("id değişken değeri: %d", id);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

id değişken değeri: 21
id değişken değeri: 22
id değişken değeri: 23

Program, başlangıçta 21 sayısını atadığı id int değişken değerini önce normal yöntemle, sonra artırma işlemcisi yoluyla 1 sayı artırarak elde ettiği bütün değerleri ekrana yazar.

++ işlemcisi değişken adından sonra kullanıldığında, artırılmış değişken değeri, artırma işleminin yapıldığı işlem satırında sonraki işlem satırlarında geçerlidir. ++ işlemcisi değişken adından önce kullanıldığında ise, artırılmış değişken değeri, artırma işleminin yapıldığı işlem satırında geçerlidir.

Artırma işlemcisine benzer şekilde, bir değişken değerini birer birer azaltan ve iki adet eksi (-) işaretinden oluşan bu işlemciye azaltma işlemcisi adı verilir.

değişken-adı--;

Eksi işaretlerinin arasında boşluk olmamalıdır. Aşağıda yer alan her iki işlem satırı da aynı işlemi gerçekleştirir:


id = id - 1;
id--;

Yukarıdaki iki işlem satırı arasında yaptıkları işlem açısından hiçbir fark yoktur. İlk satırdaki id1=id1-1 ifadesinin yerine ikinci satırda id1-- ifadesi kullanılmıştır. Şimdi, konuyu bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int id;

  id = 54;
  printf("id değişken değeri: %d\n", id);

  id = id - 1; // 53
  printf("id değişken değeri: %d\n", id);

  id--; // 52
  printf("id değişken değeri: %d", id);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

id değişken değeri: 54
id değişken değeri: 53
id değişken değeri: 52

Program, başlangıçta 54 sayısını atadığı id int değişken değerini önce normal yöntemle, sonra artırma işlemcisi yoluyla 1 sayı azaltarak elde ettiği bütün değerleri ekrana yazar.

? işlemcisi

C dilinde kullanılan işlemcilerden biri de, if yapısı yerine kullanılabilen ? işlemcisidir. ? işlemcisinin genel yapısı aşağıda gösterildiği şekildedir:

koşul ? ifade1 : ifade2;

? işlemcisi en az 3 değer gerektirir. ? işaretinden önce bir koşul, ? işaretinden sonra ise arasında : işareti bulunan iki ifade yer almalıdır. Koşul sonucunda elde edilen değer 1 (doğru) veya 0 (yanlış) olmalıdır. İfade bir sabit, değişken veya sabit ve değerlerin karışımından oluşan bir değerdir.

? işlemcisinin kullanılmasını örnekler üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int id1, id2, id3;

  id1 = 44;
  id2 = 71;

  id3 = (id1 < id2) ? 1 : 0;

  printf("id3 değişken değeri: %d", id3);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

id3 değişken değeri: 1

Program, id1 ve id2 adlı 2 değişkene atadığı değerleri < ilişkisel işlemcisi ile karşılaştırır. id1 değişken değeri id2 değişken değerinden küçük olduğu için 1 değerini id3 değişkenine atar ve ekrana yazar. Eğer id1 değeri küçük olsaydı ekrana 0 değerini yazacaktı. Burada, ? işlemcisi ile elde edilen sonuç bir değişkene atanmıştır.

Atama işlemcisi

Normal koşullarda, atama işlemcisini kullanarak tek bir değişkene değer atayabiliriz.


int id;
id = 17;

Aynı zamanda, aynı işlem satırında birden fazla değişkene değer atayabiliriz. Aşağıdaki işlem satırı id1, id2 ve id3 değişkenlerine 26 değerini atar:


id1 = id2 = id3 = 26;

Yukarıdaki işlem satırında önce 26 değeri id3 değişkenine, id3 değişken değeri id2 değişkenine ve id2 değişken değeri id1 değişkenine atanır. C'de, aşağıdaki işlem satırındaki örneğe benzer atamaları daha pratik bir şekilde yapabiliriz:

Bir değişken değerinde değişiklik yapmak için kullandığımız atama işlemcisi ve diğer işlemcileri iki farklı yapıda kullanabiliriz:

değişken-adı işlemci = değişken-adı + ifade;

değişken-adı işlemci= ifade;

Yukarıda yer alan her iki tanımlama da aynı işlemi gerçekleştirmektedir. İfade ise, bir değişken, sabit veya değişken veya sabitlerin ortaklaşa kullanılarak oluşturulduğu değerleri gösterir.

İkinci işlem satırında yer alan atamalarda, işlemci ile = işareti arasında boşluk bırakılmamalıdır.


id = id + 5;
id += 5;

Yukarıdaki işlem satırında yer alan işlemci ifadesi yerine aşağıdaki işlemcilerden birini kullanabiliriz:

+ - * / % << >> & | ^

Şimdi, öğrendiklerimizi bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int id1, id2, id3;

  id1 = id2 = id3 = 1849;

  printf("id1 değişken değeri: %d\n", id1);
  printf("id2 değişken değeri: %d\n", id2);
  printf("id3 değişken değeri: %d", id3);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

id1 değişken değeri: 1849
id2 değişken değeri: 1849
id3 değişken değeri: 1849

Program, üç adet int değişken bildirimi yapar. Tek işlem satırında her üç değişkene atama işlemcisini kullanarak aynı değeri atar. Değişken değerlerini ekrana yazar.

Geçici veri türü değiştirme işlemcisi (type cast)

Geçici veri türü değiştirme işlemcisini kullanarak bazen bir değişkenin veri çeşidini geçici olarak değiştirebiliriz. Bu değişkenin genel kullanımı aşağıda gösterilmektedir:

(veri-türü) değişken;

Şimdi, bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  int id1, id2;
  double dd1, dd2;

  id1 = 29;
  id2 = 77;

  dd1 = 52.281;
  dd2 = 79.9769;

  printf("%d %d\n", id1, id2);
  printf("%.3f %.4f\n\n", dd1, dd2);

  printf("%.4f %.4f\n", (double) id1, (double) id2);
  printf("%d %d", (int) dd1, (int) dd2);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

29 77
52.281 79.9769

29.0000 77.0000
52 79

Program, önce iki adet int ve iki adet double değişken değerini normal olarak, sonra geçici veri türü değiştirme işlemcisini kullanarak int değişken değerlerini double, double değişken değerlerini ise int değer olarak ekrana yazar.

Bir atama işleminde, atama işaretinin sol tarafında geçici veri türü değiştirme işlemcisini kullanamayız.

sizeof İşlemcisi

sizeof işlemcisini kullanarak herhangi bir veri türünün veya bir değişkenin byte olarak boyutunu elde edebiliriz. Aşağıdaki ilk satır bir veri türünün, ikinci satır ise bir değişkenin boyutunun hesaplanmasında kullanılan sizeof işlemcisinin genel yapısını göstermektedir:

sizeof (veri-türü);

sizeof değişken-adı;

sizeof ifadesi veri türü ile kullanıldığında parantezlerle birlikte tanımlanmasına rağmen, değişken adı ile kullanıldığı zaman, parantezlere gerek yoktur.

Şimdi, sizeof işlemcisinin kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  char cd;
  int id;
  float fd;
  double dd;

  printf("char veri türü boyutu: %d\n", sizeof(char));
  printf("int veri türü boyutu: %d\n", sizeof(int));
  printf("float veri türü boyutu: %d\n", sizeof(float));
  printf("double veri türü boyutu: %d\n\n", sizeof(double));

  printf("cd değişken boyutu: %d\n", sizeof cd);
  printf("id değişken boyutu: %d\n", sizeof id);
  printf("fd değişken boyutu: %d\n", sizeof fd);
  printf("dd değişken boyutu: %d", sizeof dd);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

char veri türü boyutu: 1
int veri türü boyutu: 4
float veri türü boyutu: 4
double veri türü boyutu: 8

cd değişken boyutu: 1
id değişken boyutu: 4
fd değişken boyutu: 4
dd değişken boyutu: 8

Program, 4 farklı veri türünün boyutunu hem veri türü hem de değişken adı yoluyla hesaplayarak ekrana yazar.

İşlemci Öncelikleri

Şimdiye kadar incelediğimiz bütün işlemcilerin öncelik dereceleri aşağıda yer almaktadır:

() [] -> .
! ~ ++ -- - (veri-türü) * & sizeof
* / %
+ -
<< >>
< <= > >=
== !=
<
^
|
<<
||
?
= += -= *= /= %=
,

C, yaptığı bütün işlemleri yukarıdaki işlemci önceliği kuralına uygun olarak yapar.