C Programlama

Ek Bilgiler

Bileşimler (Unions)

Bileşim, iki veya daha fazla değişken tarafından kullanılan tek bir bellek birimidir. Burada bahsi geçen değişkenler farklı veri türünden olabilir. Ancak, aynı bellek bölgesini paylaşan değişkenlerden sadece bir tanesi aynı anda bellek bölgesini kullanabilir. Genel görünüşü ile yapılara benzeyen bileşimlerin genel yapısı şekli aşağıdaki şekildedir:

union adı {
   veri-türü eleman1;
   veri-türü eleman2;
   .
   .
   .
   veri-türü elemanN;
} değişken-listesi;

Yukarıda görülen union kelimesi bir bileşim tanımlar. adı ifadesi ise bileşimin adını göstermektedir. veri-türü ifadesi C'de kullanılan herhangi bir veri türünü gösterir. değişken-listesi bileşim için tanımlanan değişken değerleri göstermektedir. adı veya değişken-listesi ifadelerinden birinin mutlaka tanımlanması gerekir.

Bileşimin elemanlarına işlem yapmak için, yapı kavramında olduğu gibi, . veya -> işaretlerinden birini kullanabilirsiniz.

Bir bileşim içinde farklı veri türünden değişkenler tanımladığınızda, program bileşimdeki en büyük değişken veri türü değeri kadar bellekte yer ayırır. Örneğin, aşağıdaki bileşim tanımlaması bellekte 8 byte'lık bir yer ayırır. Siz sadece bir int değer ataması yapsanız bile bileşim 8 byte'lık bir bellek alanı işgal eder. Ancak, int değer söz konusu bellek alanının ilk 4 byte'ını kullanır (32 bit bilgisayarlar için):

union bir1 {
   int id1;
   char cd1;
   double dd1;
};

Yapı içinde yer alan her bir eleman yapı boyutuna kendi boyutu kadar ekleme yapar. Ancak, bileşim içindeki elemanlar bileşim boyutuna ekleme yazmaz. Bileşimin boyutu, bileşim içinde yer alan en büyük boyuta sahip eleman boyutuna eşittir.

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

#include <stdio.h>
#include <string.h>

union bir1 {
  int id1;
  double dd1;
  char cd1;
} bd1;

void main (void)
{
  bd1.id1 = 21;
  bd1.cd1 = 'A';

  printf("%d %c\n", bd1.id1, bd1.cd1);  /* 1 */

  bd1.id1 = 127;
  printf("%d %c\n", bd1.id1, bd1.cd1);  /* 2 */

  bd1.dd1 = 34.75;
  printf("%d %f", bd1.id1, bd1.dd1);    /* 3 */
}

Yukarıdaki örnekte, program 1 sayısı ile gösterilen işlem satırında bir1 bileşiminde yer alan cd1 değişkeninin, 2 sayısı ile gösterilen işlem satırında id1 değişkeninin ve 3 sayısı ile gösterilen işlem satırında dd1 değişkeninin değerini normal olarak ekrana yazar. Aynı işlem satırlarında bahsi geçen değişkenler dışında kalan değerleri istendiği şekilde ekrana yazmaz, çünkü aynı bileşim içinde yer alan değişkenlerden aynı anda sadece bir tanesi ayrılan bellek bölgesini kullanabilir. Bu nedenle, sadece en son olarak atama yapılan bileşim değişkeni ekrana normal olarak yazılabilir.

#include <stdio.h>
#include <string.h>

struct yap1 {
  int id1;
  int id2;
  int id3;
  double dd1;
  char cd1;
  char cd2;
  char cd3;
} yd1;

union bir1 {
  int id1;
  int id2;
  int id3;
  double dd1;
  char cd1;
  char cd2;
  char cd3;
} bd1;

void main (void)
{
  printf("Yapı boyutu: %d\nBileşim boyutu: %d", sizeof(yd1), sizeof(bd1));
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

Yapı boyutu: 32
Bileşim boyutu: 8

Yukarıdaki örnekte, program aynı veri türlerinden ve aynı sayıda elemana sahip birer adet yapı ve bileşim tanımlar. Yapı ve bileşim boyutlarını ekrana yazar. Aynı elemanlara sahip olmalarına rağmen yapıya eklenen elemanların yapı boyutunu artırdığına, bileşim elemanlarının ise sadece en büyük olanının bileşim boyutunu belirlediğine dikkat ediniz.

#include <stdio.h>

void fonk1 (char cd1);
char fonk2 (char cd1);

void main (void)
{
  char cd1 = 87;  /* 'W' 0101 0111 */
  char cd2;

  printf("Karakter değeri: %c %3d  ", cd1, cd1);  
  fonk1(cd1);
  cd2 = fonk2(cd1);
  printf("Karakter değeri: %c %3d  ", cd2, cd2); /* 'u' 0111 0101 */
  fonk1(cd2);  
}

void fonk1 (char cd1)
{
  int id1;

  for (id1=128; id1>0; id1/=2) {
       if (cd1&id1) printf("1 ");
       else printf("0 ");
  }
  printf("\n");
}

char fonk2 (char cd1)
{
  struct yap1 {
    char cd1:4;
    char cd2:4;
  };

  union bir1 {
    char cd1;
    struct yap1 yd1;
  } bd1;
  
  char cd2;
  
  bd1.cd1 = cd1;              /* 1 */

  cd2 = bd1.yd1.cd1;          /* 2 */
  bd1.yd1.cd1 = bd1.yd1.cd2;  /* 3 */
  bd1.yd1.cd2 = cd2;          /* 4 */

  return bd1.cd1;
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

Karakter değeri: W  87  0 1 0 1 0 1 1 1 
Karakter değeri: u 117  0 1 1 1 0 1 0 1 

Yukarıdaki örnekte, program char bir değişkene atadığı 'W' değerini karakter, onluk sayı sistemi ve ikili sayı sisteminde sayı olarak ekrana yazar. Sonra, fonk2() fonksiyonu yoluyla değişkenin ilk 4 bit'ini son 4 bit'i ile değiştirerek elde edilen 'u' değerini aynı şekilde tekrar ekrana yazar.

fonk2() fonksiyonu içinde her işlem satırında yapılan değişken değerleri aktarım işlemleri aşağıdaki şekilde gösterilmektedir. fonk2() fonksiyonu içindeki cd2 değişkeni geçici veri depolama değişkeni olarak kullanılmaktadır.

#include <stdio.h>
#include <conio.h>

struct yap1 {
  unsigned uid1: 1;
  unsigned uid2: 1;
  unsigned uid3: 1;
  unsigned uid4: 1;
  unsigned uid5: 1;
  unsigned uid6: 1;
  unsigned uid7: 1;
  unsigned uid8: 1;
};

union bir1 {
  char cd1;
  struct yap1 yd1;
} bd1;

void fonk1 (char cd1);

void main (void)
{
  bd1.cd1 = 65; /* 'A' 0100 0001 */
  printf("\nKarakter değeri: %c %d  ", bd1.cd1, bd1.cd1);

  if (bd1.yd1.uid8) printf("1 ");
  else printf("0 ");
  if (bd1.yd1.uid7) printf("1 ");
  else printf("0 ");
  if (bd1.yd1.uid6) printf("1 ");
  else printf("0 ");
  if (bd1.yd1.uid5) printf("1 ");
  else printf("0 ");
  if (bd1.yd1.uid4) printf("1 ");
  else printf("0 ");
  if (bd1.yd1.uid3) printf("1 ");
  else printf("0 ");
  if (bd1.yd1.uid2) printf("1 ");
  else printf("0 ");
  if (bd1.yd1.uid1) printf("1 ");
  else printf("0 ");
  
  printf("\nKarakter değeri: %c %d  ", bd1.cd1, bd1.cd1);
  fonk1 (bd1.cd1);
}

void fonk1 (char cd1)
{
  int id1;

  for (id1=128; id1>0; id1/=2) {
       if (cd1&id1) printf("1 ");
       else printf("0 ");
  }
  printf("\n");
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

Karakter değeri: A 65  0 1 0 0 0 0 0 1 
Karakter değeri: A 65  0 1 0 0 0 0 0 1 

Yukarıdaki örnekte, program char bir değişkene atadığı 'A' değerini karakter, onluk sayı sistemi ve bileşim yöntemi kullanarak ikili sayı sisteminde sayı olarak ekrana yazar. Sonra, tekrar aynı işlemi yapar, ancak bu kez ikili sayı sisteminde yazma işlemini farklı bir yöntemle gerçekleştirir.