BG MVC Model View Controller eğitim serisi yayında...

Ana sayfa > Programlama > C Programlama > Yapılar (Structures)

Yapılar (Structures)

Daha önce, aynı türden olan verileri diziler içinde tanımladıktan sonra, sadece bir dizi ismini kullanarak bu verilere erişim sağladığımızı gördük. Türleri farklı olan verileri tek bir isim altında toplayabilmemiz için ise, yapı (structure) adı verilen kullanıcı tanımlı veri türlerini kullanabiliriz.

Yapı, farklı veri türlerini bir grup altında toplayan kullanıcı tanımlı bir veri türüdür. İki veya daha fazla elemandan oluşur. Bir yapı içindeki bütün veri tipleri aynı olabileceği gibi, her veri türü birbirinden farklı da olabilir.

Yapı bildirimi

Yapıları tanımlamak için aşağıda gösterilen üç yöntemden birini kullanabiliriz::


// 1. Veri yapısı ve değişkenleri birlikte tanımlama
struct adı {
   veri-türü eleman1;
   veri-türü eleman2;
   .
   .
   .
   veri-türü elemanN;
} değişken-listesi;

// 2. Veri yapısı ve değişkenleri ayrı ayrı tanımlama
struct adı {
   veri-türü eleman1;
   veri-türü eleman2;
   .
   .
   .
   veri-türü elemanN;
} 

// struct adı değişken;
3. Yapı adı kullanmadan sadece değişken kullanarak yapı tanımlama

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

Yapı bildiriminde, adı veya değişken-listesi ifadelerinden birisi mutlaka tanımlanmış olmalıdır. Yukarıdaki her üç yöntemde de, bu kural uygulanmaktadır.

Yukarıda görülen struct kelimesi bir yapı tanımlar. adı ifadesi ise yapının adını göstermektedir. veri-türü ifadesi C'de kullanılan herhangi bir veri türünü gösterir. değişken-listesi ifadesi yapı için tanımlanan değişkenleri göstermektedir.

Yukarıda kullanılan yapı tanımlama yönteminin ilkinde yapı ve yapı değişken değerleri birlikte tanımlanır. İkinci yöntemde, önce yapı daha sonra yapı değişken değeri tanımlanmaktadır. Üçüncü yöntemde ise, yapı adı kullanmadan sadece değişken adı kullanarak yapı tanımlanmaktadır. Her üç yöntem de aynı sonucu vermektedir.

Eğer sadece adı ifadesi kullanılırsa, veri türünün sadece yapısı oluşturulmuş olur. Değişken bildirimi ayrıca yapılmalıdır.

Bir yapı içinde yer alan bölümlere eleman veya alan adı verilir.

Bir yapı içindeki veriler birbiri ile mantıksal olarak ilişkilidir:


struct yap {
  char cdizi1[20]; // Adı
  char cdizi2[20]; // Soyadı
  char cdizi3[20]; // Memleketi
  char cdizi4[20]; // Tahsili
  int id;          // Yaşı
} yd;

Yukarıdaki ifadeler, dört adedi karakter dizisi ve bir adedi int bir değer olmak üzere toplam beş elemandan oluşan yap adlı bir yapı ve bu yapı için ayrıca yd adlı bir değişken tanımlar. İstediğimiz sayıda tanımlayabileceğimiz bu değişken yoluyla yapının elemanlarına ulaşabiliriz.

Yapı elemanlarına değer atama

Bir yapı ve yapı değişkeni oluşturulduktan sonra, yapı içinde yer alan bir elemana yapı değişkeni yolu ile ulaşmak için aşağıdaki ifadeyi kullanabiliriz:

yapı-değişkeni.alan-adı

Yapı değişkeni kullanımını gösteren farklı uygulamalar aşağıda gösterilmektedir:


// yap adlı yapı ve yd adlı yapı değişkeni bildirimi
struct yap {
  char cdizi1[20]; // Adı
  char cdizi2[20]; // Soyadı
  char cdizi3[20]; // Memleketi
  char cdizi4[20]; // Tahsili
  int id;          // Yaşı
} yd;

// yap adlı yapının id değişkenine 21 değeri atar.
yd.id = 21;

// yap adlı yapının id değişken değerini ekrana yazma
printf("%d", yd.id);

// yap adlı yapının id değişkenine klavyeden girilen bir değeri okutma
scanf("%d", &yd.id);

// yap adlı yapının cdizi1 dizisine bir değer girme ve aynı değeri ekrana yazdırma
printf("Adını giriniz: ";
fgets(yd.cdizi1, 20, stdin);
printf("%s", yd.cdizi1);

// yap adlı yapının cdizi1 dizisindeki 2.karakteri ekrana yazma
printf("%c", yd.cdizi1[1]);
 

Yapılar sayesinde tek bir değişken yolu ile birden fazla farklı türden veriye tek isim yoluyla erşim sağlayabiliriz.

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


#include <stdio.h>

struct yap {
  char cdizi1[20]; // Adı
  char cdizi2[20]; // Soyadı
  char cdizi3[20]; // Memleketi
  char cdizi4[20]; // Tahsili
  int id;          // Yaşı
} yd;

char* bg_fgets(char *str, int count);

int main(void)
{
  printf("Adı: ");
  bg_fgets(yd.cdizi1, 20);
  printf("Soyadı: ");
  bg_fgets(yd.cdizi2, 20);
  printf("Memleketi: ");
  bg_fgets(yd.cdizi3, 20);
  printf("Tahsili: ");
  bg_fgets(yd.cdizi4, 20);
  printf("Yaşı: ");
  scanf("%d", &yd.id);

  printf("%s %s %s %s %d", yd.cdizi1, yd.cdizi2, yd.cdizi3, yd.cdizi4, yd.id);

  return 0;
}

char* bg_fgets(char *str, int count)
{
  const char *s;

  fgets(str, count, stdin);

  for (s=str; *s && *s!='\n'; ++s);

  if ((s-str) < (count-1)) *(str+(s-str)) = '\0';

  return str;
}

Yukarıdaki örnekte, program bir kişi için girdiğiniz verileri yap adlı bir yapının elemanlarına atar ve elemanları ekrana yazar. Bu durumda yapı elemanlarına daima yd değişkeni yoluyla ulaşılır.

bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda, karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.

Bir yapı içinde tanımlanan değişkenler program içinde yer alan diğer değişkenlerle aynı ada sahip olsalar bile, program değişkenleri farklı bir şekilde kabul eder. Bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  struct yap {
    int id;
    char cd;
  } yd;

  int id = 21;

  yd.id = 192;
  yd.cd = 'A';

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

  printf("yap adlı yapı içindeki id değişken değeri: %d\n", yd.id);
  printf("yap adlı yapı içindeki cd değişken değeri: %c", yd.cd);

  return 0;
}

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

main() fonksiyonu id değişken değeri: 21
yap adlı yapı içindeki id değişken değeri: 192
yap adlı yapı içindeki id değişken değeri: A

Program, yapı içinde yer alan id değişkeni ile yapı dışında yer alan id değişkenine tamamen farklı değişkenler olarak işlem yapar.

Yapılar için değişken tanımlama

Yukarıda yap adlı bir yapı bildirimi yaparken aynı zamanda yd adlı bir değişken tanımladık. yap yapısının bildirimini yaptıktan sonra, program içindeki işlem satırları ile aynı yapı için tekrar değişken tanımlayabiliriz. Bir yapıyı bir kez tanımladıktan sonra, aşağıdaki ifade ile daha fazla değişken tanımlayabiliriz:

struct yapı-adı değişken-listesi;

Örneğin aşağıdaki ifadeyi kullanarak yap yapısı için 2 değişken tanımlayabiliriz:


struct yap yd2, yd3;

Bu işlem satırı, yapı tanımlamasından bağımsız olarak değişken tanımlama olanağı sağlar. Bu özellikten dolayı, yapı tanımlamalarında değişken tanımlamak şart değildir. Bazı durumlarda yapı tanımlamalarında yapı adı tanımlamayabilirsiniz. Ancak, bu durumda değişken adlarını mutlaka tanımlamamız gerekir:


struct {
  char cdizi[10];
  int id;
  char cd;
} yd1, yd2, yd3;

Yapı tanımlamalarında yapı-adı ve değişken-listesi ifadelerinin en az birisini kullanmamız gereklidir. İkisini birlikte kullanmak ise isteğe bağlıdır.

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


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

struct yap1 {
  char cdizi[20];
  int id;
} yd1;

struct yap2 {
  char cdizi[20];
  int id;
};

struct {
  char cdizi[20];
  int id;
} yd3;

struct yap2 yd2;

char* bg_fgets(char *str, int count);

int main(void)
{
  char cdizi[20];

  printf("Bir karakter dizisi giriniz: ");
  bg_fgets(yd1.cdizi, 20);
  printf("int bir değer giriniz: ");
  bg_fgets(cdizi, 20);
  yd1.id = (int) atoi(cdizi);

  printf("\nBir karakter dizisi giriniz: ");
  bg_fgets(yd2.cdizi, 20);
  printf("int bir değer giriniz: ");
  bg_fgets(cdizi, 20);
  yd2.id = (int) atoi(cdizi);

  printf("\nBir karakter dizisi giriniz: ");
  bg_fgets(yd3.cdizi, 20);
  printf("int bir değer giriniz: ");
  bg_fgets(cdizi, 20);
  yd3.id = (int) atoi(cdizi);

  printf("%s %d\n", yd1.cdizi, yd1.id);
  printf("%s %d\n", yd2.cdizi, yd2.id);
  printf("%s %d", yd3.cdizi, yd3.id);

  return 0;
}

char* bg_fgets(char *str, int count)
{
  const char *s;

  fgets(str, count, stdin);

  for (s=str; *s && *s!='\n'; ++s);

  if ((s-str) < (count-1)) *(str+(s-str)) = '\0';

  return str;
}

Program, 3 farklı yapı tanımlar. yap1 yapısının bildirimi hem yapı adı hem de değişken, yap2 yapısının bildirimi sadece yapı adı ve yap3 yapısının bildirimi ise sadece değişken adı tanımlanarak yapılmıştır. Girilen değerleri bu yapı içindeki elemanlara atayarak ekrana yazar.

bg_fgets() fonksiyonu, fgets() fonksiyonu ile klavyeden okunan karakter dizisi girilebilecek maksimum karakter sayısından az olduğunda, karakter dizisine '\0' karakterinden hemen önce otomatik olarak eklenen '\n' karakteri yerine '\0' karakteri koymak için kullanılır.

Yapı değişken dizileri oluşturma

Yapılar için kullanacağınız değişkenleri bir dizi içinde tanımlayabiliriz. Bu işlemi aşağıdaki şekilde yapabilirsiniz:


struct yap {
  char cdizi1[20];
  char cdizi2[20];
  int id;
} ydizi[10];

Yukarıdaki işlem satırları 3 elemanlı ve yap adlı bir yapı ve bu yapı için, bir dizinin elemanları olarak, 10 adet değişken tanımlar. Burada, üçüncü yapı değişkeni ile id değişkenine aşağıdaki işlem satırı ile 21 değeri atanır:


ydizi[2].id = 21;


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

struct yap {
  char cdizi1[20]; // İsim
  char cdizi2[20]; // Soyadı
  int id;          // Yaşı
} ydizi[5];

char* bg_fgets(char *str, int count);

int main(void)
{
  int id;
  char cdizi[20];

  for (id=0; id<5; id++) {
       printf("Adı: ");
       bg_fgets(ydizi[id].cdizi1, 20);
       printf("Soyadı: ");
       bg_fgets(ydizi[id].cdizi2, 20);
       printf("Yaşı: ");
       bg_fgets(cdizi, 20);
       ydizi[id].id = (int) atoi(cdizi);

       printf("\n");
  }

  for (id=0; id<5; id++) {
       printf("%s %s %d\n", ydizi[id].cdizi1, ydizi[id].cdizi2, ydizi[id].id);
  }

  return 0;
}

char* bg_fgets(char *str, int count)
{
  const char *s;

  fgets(str, count, stdin);

  for (s=str; *s && *s!='\n'; ++s);

  if ((s-str) < (count-1)) *(str+(s-str)) = '\0';

  return str;
}

Program, yap adlı bir yapı ve bu yapı için bir dizi içinde 5 adet değişken tanımlar. Sonra, bu değişkenler yoluyla yapıya girilen verileri ekrana yazar.


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

struct yap {
  char cdizi1[20]; // İsim
  char cdizi2[20]; // Soyadı
  int id;          // Yaşı
} ydizi[5];

char* bg_fgets(char *str, int count);

int main(void)
{
  FILE *fp;
  int id;
  char cdizi[20];

  if ((fp = fopen("deneme.txt", "wb")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  for (id=0; id<5; id++) {
       printf("Adı: ");
       bg_fgets(ydizi[id].cdizi1, 20);
       printf("Soyadı: ");
       bg_fgets(ydizi[id].cdizi2, 20);
       printf("Yaşı: ");
       bg_fgets(cdizi, 20);
       ydizi[id].id = (int) atoi(cdizi);

       printf("\n");
  }

  fwrite(ydizi, sizeof ydizi, 1, fp);
  fclose(fp);

  if ((fp = fopen("deneme.txt", "rb")) == NULL) {
      printf("Dosya açılamadı!\n");
      exit(1);
  }

  fread(ydizi, sizeof ydizi, 1, fp);

  for (id=0; id<5; id++) {
       printf("%-20s %-20s %-2d\n", ydizi[id].cdizi1, ydizi[id].cdizi2, ydizi[id].id);
  }

  fclose (fp); // Dosya kapatma

  return 0;
}

char* bg_fgets(char *str, int count)
{
  const char *s;

  fgets(str, count, stdin);

  for (s=str; *s && *s!='\n'; ++s);

  if ((s-str) < (count-1)) *(str+(s-str)) = '\0';

  return str;
}

Program, yap adlı bir yapı ve bu yapı için bir dizi içinde 5 adet değişken tanımlar. Sonra, bu değişkenler yoluyla yapıya girilen verileri deneme.txt adlı dosyaya yazar. Daha sonra, kayıtları dosyadan okuyarak ekrana yazar.

Yapılara ilk değer atama

Bir yapı değişkeninin bildirimini yaparken ilk değer atama yöntemini kullanarak, yapı elemanlarına birer değer atayabiliriz. Bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  char cdizi1[20]; // Adı
  char cdizi2[20]; // Soyadı
  char cdizi3[20]; // Memleketi
  char cdizi4[20]; // Tahsili
  int id;          // Yaşı
} yd = { "Murat", "Kalender", "Ankara", "Lise", 21 };

int main(void)
{
  printf("%s %s %s %s %d", yd.cdizi1, yd.cdizi2, yd.cdizi3, yd.cdizi4, yd.id);

  return 0;
}

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

Murat Kalender Ankara Lise 21

Program, yap adlı bir yapı bildirimi yapar. yap yapısı için yd adlı değişkeninin bildirimini yaparken, yap yapısının elemanlarına ilk değer atama yöntemi ile bazı değerler atar. Sonra, yapı eleman değerlerini ekrana yazar.

Şimdi, yukarıdaki örneği biraz değiştirerek incelemeye çalışalım:


#include <stdio.h>

struct yap {
  char cdizi1[20]; // Adı
  char cdizi2[20]; // Soyadı
  char cdizi3[20]; // Memleketi
  char cdizi4[20]; // Tahsili
  int id;          // Yaşı
};

int main(void)
{
  struct yap yd = { "Murat", "Kalender", "Ankara", "Lise", 21 };

  printf("%s %s %s %s %d", yd.cdizi1, yd.cdizi2, yd.cdizi3, yd.cdizi4, yd.id);

  return 0;
}

Program, bir önceki programın yaptığı işlem ile aynı işlemi gerçekleştirir. Tek fark yapı değişken bildirim işleminin main() fonksiyonu içinde yapılmış olmasıdır.

Bir yapı için tanımlanan birden fazla değişkene ilk değer atama yöntemi ile değerler atayabiliriz. Bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  char cdizi1[20]; // Adı
  char cdizi2[20]; // Soyadı
  char cdizi3[20]; // Memleketi
  char cdizi4[20]; // Tahsili
  int id;          // Yaşı
} ydizi[4] = { "Ahmet", "Efendi", "Rize", "Lise", 25,
               "Mehmet", "Sakin", "Kars", "Ortaokul", 36,
               "Mustafa", "Kibar", "Niğde", "Lise", 42,
               "Murat", "Sade", "Sinop", "Üniversite", 28
             };

int main(void)
{
  int id;

  for (id=0; id<4; id++)
       printf("%-20s %-20s %-20s %-20s %-d\n", 
       ydizi[id].cdizi1, ydizi[id].cdizi2, ydizi[id].cdizi3, 
       ydizi[id].cdizi4, ydizi[id].id);  

  return 0;
}

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

Ahmet Efendi Rize Lise 25
Mehmet Sakin Kars Ortaokul 36
Mustafa Kibar Niğde Lise 42
Murat Sade Sinop Üniversite 28

Bir yapı geri veren fonksiyonlar

Fonksiyonlar bir yapı geri döndürebilirler. Şimdi, bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  int id;
  char cd;
} yd;

struct yap fonk(void);

int main(void)
{
  yd = fonk();

  printf("%d %c", yd.id, yd.cd);

  return 0;
}

struct yap fonk(void)
{
  struct yap yd;

  yd.id = 21;
  yd.cd = 'A';

  return yd;
}

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

21 A

Program, bir yapı tanımlar. fonk() fonksiyonu içinde yapı elemanlarına birer değer atar. fonk() fonksiyonu bu değerleri geri verir. Program geri verilen değerleri ekrana yazar.

Yapı değişken değerlerini diğer yapı değişkenlerine atama

Bir yapı değişkeninin içeriğini aynı veri türünden başka bir yapı değişkenine atayabiliriz. Bu özelliği, bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  char cdizi1[20]; // Adı
  char cdizi2[20]; // Soyadı
  int id;          // Yaşı
};

int main(void)
{
  struct yap yd1 = { "Murat", "Kalender", 21 };
  struct yap yd2;

  yd2 = yd1;

  printf("%s %s %d\n", yd1.cdizi1, yd1.cdizi2, yd1.id);
  printf("%s %s %d", yd2.cdizi1, yd2.cdizi2, yd2.id);

  return 0;
}

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

Murat Kalender 21
Murat Kalender 21

Program yap adlı yapıya yd1 değişkeni yolu ile bazı veriler atar. yd1 değişken içeriğini aynı yapı için tanımlanan yd2 değişkenine atadıktan sonra, her iki değişken içeriğini ekrana yazar. Sonuç olarak, her iki değişken içeriğinin aynı verilerden oluştuğu görülür.

Yapı boyutunun hesaplanması

Bir program içinde tanımlanan yapının boyutlarını sizeof işlemcisini kullanarak hesaplayabiliriz. Bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  int id1;
  int id2;
  char cd;
  int cdizi[10];
} yd;

int main(void)
{
  printf("yap yapısının uzunluğu: %d\n", sizeof (struct yap));
  printf("yap yapısının uzunluğu: %d", sizeof (yd));

  return 0;
}

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

yap yapısının uzunluğu: 52
yap yapısının uzunluğu: 52

Program, yap yapısının byte olarak uzunluğunu hesaplayarak 2 kez ekrana yazar. İlk yöntemde yapı adını ikinci yöntemde ise yapı değişkenini kullanır.

Yapı işaretçileri

Bir yapıya ve elemanlarına erişim için sadece bir değişken değil aynı zamanda işaretçi de kullanabiliriz.


struct yap {
  char cdizi1[20]; // Adı
  char cdizi2[20]; // Soyadı
  char cdizi3[20]; // Memleketi
  char cdizi4[20]; // Tahsili
  int id;          // Yaşı
} yd, *yp;

İşaretçi yoluyla yapı elemanlarına işlem yapmak için -> işareti kullanılır. Ancak, işaretçiyi kullanmadan önce, yapı cinsinden bir değişken tanımlamak ve değişkenin bellek adresini yapı işaretçisine atamak gerekir. yp işaretçisini kullanarak yap yapısının yd değişkenine 21 değerini atamak için aşağıdaki işlem satırlarını kullanabiliriz:


yp = &yd;
yp->id = 21;

Bir değişken yoluyla yap elemanlarına işlem yapmak için . işaretini, bir işaretçi yoluyla erişmek için -> işaretini kullanmamız gerekir.

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


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

struct yap {
  char cdizi1[20];
  char cdizi2[20];
  int id;
} yd, *yp;

int main(void)
{
  yp = &yd;

  strcpy(yd.cdizi1, "Bilgisayar");
  strcpy(yp->cdizi2, "Programlama");
  yp->id = 21;

  printf("%s %s %d", yd.cdizi1, yp->cdizi2, yp->id);

  return 0;
}

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

Bilgisayar Programlama 21

Program, yap adlı bir yapı ve bu yapı için yd adlı bir değişken ve yp adlı bir işaretçi tanımlar. yd değişkeninin adresini yp işaretçisine atar. yap yapısı içindeki cdizi1 dizisine yd değişkeni yoluyla, cdizi2 dizisi ve id değişkenine yp işaretçisi yoluyla bir değer atar. Sonra, yapı elemanlarının değerlerini ekrana yazar.


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

struct yap {
  char cdizi1[20];
  char cdizi2[20];
  int id;
} yd1, yd2, *yp1, *yp2;

int main(void)
{
  yp1 = &yd1;
  yp2 = &yd2;

  strcpy(yp1->cdizi1, "Bilgisayar");
  strcpy(yp1->cdizi2, "Programlama");
  yp1->id = 123;

  strcpy(yp2->cdizi1, "C Programlama");
  strcpy(yp2->cdizi2, "Dili");
  yp2->id = 456;

  printf("%s %s %d\n", yp1->cdizi1, yp1->cdizi2, yp1->id);
  printf("%s %s %d", yp2->cdizi1, yp2->cdizi2, yp2->id);

  return 0;
}

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

Bilgisayar Programlama 123
C Programlama Dili 456

Program, yap adlı bir yapı tanımlar. yap yapısı için iki adet değişken ve işaretçi tanımlar. yap yapısındaki dizi ve int değerlere işaretçiler yoluyla atadığı verileri yine işaretçiler yoluyla ekrana yazar.

Yapıları fonksiyonlara değer yoluyla argüman olarak geçirme

Bir yapı için tanımlanan değişkenleri kullanarak, bir yapıyı bir fonksiyona değer yoluyla argüman olarak geçirebiliriz. Bu durumda, fonksiyon içinde yapı değişkeni üzerinden yapı içeriğine yapılan değişiklikler, fonksiyon dışında yapı değerinde herhangi bir değişiklik yapmaz.

Bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  int id;
  char cd;
} yd;

void fonk(struct yap yd);

int main(void)
{
  struct yap yd;

  yd.id = 21;
  yd.cd = 'A';

  fonk(yd);

  printf("%d %c", yd.id, yd.cd);

  return 0;
}

void fonk(struct yap yd)
{
  yd.id += 5;
  yd.cd += 5;

  printf("%d %c\n", yd.id, yd.cd);
}

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

26 F
21 A

Program, bir yapı içinde bir int ve bir char değer tanımlar. Yapı elemanlarına birer değer atar. Sonra bu yapıyı, değer yoluyla argüman geçirme yöntemi kullanarak, fonk() fonksiyonuna argüman olarak geçirir. Fonksiyon içinde her bir yapı elemanına 5 değeri ekleyerek ekrana yazar. Daha sonra, main() fonksiyonu içinde yapı elemanlarının değerlerini tekrar ekrana yazar. Yapı eleman değerlerinin fonk() fonksiyonu tarafından değiştirildiği halde main() fonksiyonu içinde bu değişikliğin geçerli olmadığı görülür.


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

struct yap {
  char cdizi[40];
} yd;

void fonk(struct yap yd);

int main(void)
{
  strcpy (yd.cdizi, "Bilgisayar");
  fonk(yd);
  printf("%s", yd.cdizi);

  return 0;
}

void fonk(struct yap yd)
{
  strcpy (yd.cdizi, "Programlama");
  printf("%s\n", yd.cdizi);
}

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

Programlama
Bilgisayar

Program, içinde bir dizi bulunan bir yapıyı fonk() adlı fonksiyona argüman olarak geçirerek, dizi içeriğini değiştirir ve ekrana yazar. Daha sonra main() fonksiyonu içinde dizi içeriğini tekrar ekrana yazar. Fonksiyon içinde dizi değerinde yapılan değişikliğin main() fonksiyonu içinde geçerli olmadığı görülür.

Yapıları fonksiyonlara referans yoluyla argüman olarak geçirme

Bir yapıyı, yapı için tanımlanan işaretçiler yoluyla bir fonksiyona argüman olarak geçirebiliriz. Bu uygulamada, referans yoluyla argüman geçirme yöntemi kullanılır. Bu yöntemi kullandığımızda, yapıda yer alan değişkenlerin bellek adresleri fonksiyona argüman olarak geçirildiğinden, fonksiyon içinde yapıda yer alan elemanlara yapılan değişiklik, fonksiyonun çalışması sona erdikten sonra da geçerli olur. Şimdi, bu özelliği örnekler üzerinde incelemeye çalışalım:


#include <stdio.h>

struct yap {
  int id;
  char cd;
} yd;

void fonk(struct yap *yp);

int main(void)
{
  struct yap *yp;

  yd.id = 21;
  yd.cd = 'A';

  yp = &yd;

  fonk(yp);

  printf("%d %c", yd.id, yd.cd);

  return 0;
}

void fonk(struct yap *yp)
{
  yp->id += 5;
  yp->cd += 5;

  printf("%d %c\n", yp->id, yp->cd);
}

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

26 F
26 F

Program, bir yapı içinde bir int ve bir char değer tanımlar. Yapı elemanlarına birer değer atar. Sonra bu yapıyı, referans yoluyla argüman geçirme yöntemiyle, fonk() fonksiyonuna argüman olarak geçirir. Fonksiyon içinde her bir yapı elemanına 5 değeri ekleyerek ekrana yazar. Daha sonra, main() fonksiyonu içinde yapı elemanlarının değerlerini tekrar ekrana yazar. Yapı eleman değerlerinin fonk() fonksiyonu tarafından değiştirildiği görülür.


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

struct yap {
  int id;
  char cd;
  char cdizi[20];
} yd1, yd2, *yp1, *yp2;

void fonk(struct yap *yp1, struct yap *yp2);

int main(void)
{
  yp1 = &yd1;
  yp2 = &yd2;

  yp1->id = 541;
  yp1->cd = 'A';
  strcpy (yp1->cdizi, "Bilgisayar");

  yp2->id = 184;
  yp2->cd = 'B';
  strcpy (yp2->cdizi, "Programlama");

  printf("yd1 değişken değerleri: %d %c %s\n", yp1->id, yp1->cd, yp1->cdizi);
  printf("yd2 değişken değerleri: %d %c %s\n\n", yp2->id, yp2->cd, yp2->cdizi);    
  
  fonk(yp1, yp2);

  printf("yd1 değişken değerleri: %d %c %s\n", yp1->id, yp1->cd, yp1->cdizi);
  printf("yd2 değişken değerleri: %d %c %s", yp2->id, yp2->cd, yp2->cdizi);    

  return 0;
}

void fonk(struct yap *yp1, struct yap *yp2)
{
  struct yap yd3;

  yd3 = *yp1;
  *yp1 = *yp2;
  *yp2 = yd3;
}

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

yd1 değişken değerleri: 541 A Bilgisayar
yd2 değişken değerleri: 184 B Programlama

yd1 değişken değerleri: 184 B Programlama
yd2 değişken değerleri: 541 A Bilgisayar

Program, yap adlı bir yapı tanımlar. yap yapısı için kullandığı yp1 ve yp2 işaretçileri yoluyla yapı elemanlarına değerler atar. Atanan değerleri ekrana yazar. Daha sonra, yapı işaretçilerini fonk() adlı fonksiyona geçirerek yp1 ve yp2 işaretçilerinin adreslerini gösterdiği değişkenlerin içeriğini birbiri ile değiştirir. yap yapısının eleman değerlerini tekrar ekrana yazar.

İç içe yapılar

Bir yapının elemanlarından biri yine bir yapı olabilir. Bu durumda, iki yapı iç içe kullanılmış olur. Bu tür bir tanımlamanın örnek şekli aşağıda gösterilmektedir:


struct yap2 {
  char cdizi1[15];
  char cdizi2[15];
  int id;
};

struct yap1 {
  char cdizi1[15];
  char cdizi2[15];
  int id;
  struct yap2 y3;
} yd1, yd2;

Yukarıda, yap1 adlı bir yapı ve bu yapının içinde yap2 adlı bir yapı tanımlanmıştır. Burada, içte kalan yap2 yapısının elemanlarına erişmek için yap1 yapısından geçiş sağlanır. yap2 yapısının id değişkenine 21 değerini atamak için aşağıdaki işlem satırı kullanmamız gerekir:


yd1.y3.id = 21;

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


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

struct yap2 {
  char cdizi[30];
};

struct yap1 {
  char cdizi1[15];
  char cdizi2[15];
  int id;
  struct yap2 yd2;
} yd1;

int main(void)
{
  strcpy (yd1.cdizi1, "Bilgisayar");
  strcpy (yd1.cdizi2, "Programlama");
  yd1.id = 21;

  strcpy (yd1.yd2.cdizi, "C Programlama Dili");

  printf("%s %s %d\n", yd1.cdizi1, yd1.cdizi2, yd1.id);
  printf("%s", yd1.yd2.cdizi);
  
  return 0;
}

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

Bilgisayar Programlama 21
C Programlama Dili

Yukarıdaki örnekte, program yap1 ve yap2 yapısının elemanlarına bazı değerler verir ve bu değerleri ekrana yazar. yap2 yapısının elemanlarına erişim için yap1 değişkenini kullanır.

Bit alanları

Yapılar içinde bit alanı adı verilen elemanlar kullanabiliriz. Bir bit alanını kullanarak, bir byte veya kelimeye bit seviyesinde işlem yapabiliriz. Bir byte veya kelime içinde yer alan bit'lere erişmek için yapıları kullanabiliriz. Bit alanı, bir yapının bit olarak uzunluğunu belirleyen özel bir yapı elemanıdır.

Bit alanları, birden fazla Doğru/Yalnış değerini bir byte içine yükleme, bazı aygıtlara bir byte içindeki bit'ler yoluyla bilgi gönderme ve bit seviyesinde işlem gerektiren şifreleme işlemleri için kullanılır. Bir bit alanının genel yapısı aşağıda gösterilmektedir:


struct adı {
  veri-türü adı:boyut;
  veri-türü adı:boyut;
  .
  .
  .
  veri-türü adı:boyut;
} değişken-listesi;

Yukarıda yer alan veri-türü ifadesi int veya unsigned int bir değer olmalıdır. 1 bit uzunluğunda olan bir bit alanı mutlaka unsigned int bir değer olarak tanımlanmalıdır. Bunun nedeni tek bir bit'in işaret biti içermesinin mümkün olmamasıdır. adı ifadesi bit alanının adını, boyut ifadesi ise bit alanının boyutunu göstermektedir. Bir yapı içindeki bit alanlarına tıpkı diğer yapı elemanları gibi erişilir.

Bir bit alanın değeri 1 ile 16 bit arasında tanımlanmalıdır.

Şimdi, bit alanlarının kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <stdio.h>

int main(void)
{
  struct yap {
    unsigned int uid1:3;
    unsigned int uid2:3;
    unsigned int uid3:2;
  } yd;

  yd.uid1 = 4;
  yd.uid2 = 3;
  yd.uid3 = 2;

  printf("%d %d %d", yd.uid1, yd.uid2, yd.uid3);

  return 0;
}

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

4 3 2

Program yap adlı bir yapı ve bu yapı içinde uid1, uid2 ve uid3 adlı unsigned bit alanları tanımlar. Bit alanlarına sıra ile 4, 3 ve 2 değerlerini atayarak ekrana yazar.


15  14  13  12  11  10  9  8  7  6  5  4  3  2  1  0
----------------------------  ----  -------  -------

           boş                uid3    uid2     uid1

                              1 0    0 1 1    1 0 0
                              ----  -------  -------
                               2       3        4

yap yapısı içindeki bit alanlarına atanabilecek azami değerler aşağıda yer almaktadır:

uid1 bit alanının alabileceği azami değer:  1 1 1 = 7
uid2 bit alanının alabileceği azami değer:  1 1 1 = 7
uid3 bit alanının alabileceği azami değer:    1 1 = 3

Bu azami değerler bit alanlarının genişliğine bağlı olarak belirlenir. Örnekte yer alan bit alanlarının ikisi tanesi 3, bir tanesi ise 2 bit genişliğindedir. Bu hesaplamalar her bit hanesinin 1 olduğu kabul edilerek yapılmıştır.

Bir yapı içinde, bit alanlarını diğer yapı elemanları ile birlikte kullanabiliriz. Bu özelliği bir örnek üzerinde incelemeye çalışalım:


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

int main(void)
{
  struct yap {
    char cdizi[15];
    unsigned int uid1:4;
    unsigned int uid2:5;
    unsigned int uid3:3;
  } ydizi[2];

  ydizi[0].uid1 = 12;
  ydizi[0].uid2 = 25;
  ydizi[0].uid3 = 5;
  strcpy (ydizi[0].cdizi, "Bilgisayar");

  ydizi[1].uid1 = 9;
  ydizi[1].uid2 = 18;
  ydizi[1].uid3 = 4;
  strcpy (ydizi[1].cdizi, "Programlama");

  printf("%d %d %d %s\n", ydizi[0].uid1, ydizi[0].uid2, ydizi[0].uid3, ydizi[0].cdizi);
  printf("%d %d %d %s", ydizi[1].uid1, ydizi[1].uid2, ydizi[1].uid3, ydizi[1].cdizi);  

  return 0;
}

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

12 25 5 Bilgisayar
9 18 4 Programlama

Program, yap adlı bir yapı tanımlar. yap yapısının içinde bir karakter dizisi ve 3 adet bit alanı tanımlar. yap için tanımlanan 2 değişken yoluyla yapı elemanlarına atadığı değerleri ekrana yazar. Bu programda da bit alanları için girilecek değerler, alan genişlikleri ile sınırlıdır.

15  14  13  12    11  10  9   8  7  6  5  4   3  2  1  0
---------------- -----------  -------------   -----------

           boş       uid3          uid2          uid1

            y1[0]   1 0 1        1 1 0 0 1      1 1 0 0
                   -------    -------------    ----------
                      5             25             12
                
            y1[1]   1 0 0        1 0 0 1 0      1 0 0 1
                  --------    -------------    ----------
                      4             18              9                

Yukarıdaki örnekte, yap1 yapısı içindeki bit alanlarına atanabilecek azami değerler aşağıda yer almaktadır:

uid1 bit alanının alabileceği azami değer: 1 1 1 1 1 = 31
uid2 bit alanının alabileceği azami değer:   1 1 1 1 = 15
uid3 bit alanının alabileceği azami değer:     1 1 1 = 7

Bit alanları ile ilgili bazı sınırlamalar mevcuttur. En düşük bellek birimi byte olduğundan bir bit alanının bellek adresini elde edemeyiz. Ayrıca bit alanlarını bir dizi içinde kullanamayız.

Eğer bir yapı içindeki alanlara çok küçük sayılar gireceksek, bit alanlarını kullanmak, özellikle veri tabanı uygulamalarında, girilen verilerin daha az yer kaplamasını sağlayacaktır. Örneğin, int bir değer olarak tanımladığınız bir yapı alanına girilecek en büyük değer 10 sayısı ise, bu alanı 4 bit'lik bir bit alanı olarak tanımlamak, sadece tek bir kayıt için, size 12 bit'lik bir boşluk sağlayacaktır.