Yapılar

C'de yapılar aynı ya da farklı tiplerden değişkenleri bir isim altında gruplamaya yarar. Bir yapının genel olarak tanımlanışı şu şekildedir:

struct yapı_ismi {
    değişken_tipi1 eleman_ismi1;
    değişken_tipi2 eleman_ismi2;
    değişken_tipi3 eleman_ismi3;
};

Örneğin birden fazla okulda ders veren bir öğretmen öğrencilerinin sınav bilgilerini bir yapı altında toplamak istesin. Bu durumda şöyle bir yapı oluşturabilir:

struct student{
    char school_name[15];
    short int number;
    char exam1;
    char exam2;
};

Bu yapıldığında herhangi bir değişken oluşturulmaz. Yukarıdaki örnek sadece bu yapıda bir gruplama yapılacağı anlamına gelir. Bu yapıda değişkenler ilan etmek ise sonrasında şu şekilde yapılır:

struct yapı_ismi değişken_ismi;

Örnek olarak Gökhan Keskin isimli öğrencinin bilgileri için bir değişken oluşturulacak olsun:

struct student gokhan_keskin;

Bu yapıldıktan sonra gokhan_keskin ismiyle tanınacak bir değişkenler grubu oluşturulur ve derleyici bu değişken için yapının içindeki tüm elemanlara yetecek kadar yer ayırır.

Değişken oluşturma hemen yapı tanımlamasından sonra da yapılabilir:

struct student{
    char school_name[15];
    short int number;
    char exam1;
    char exam2;
} gokhan_keskin, ahmet_ozturk, mehmet_sahin;

Bu durumda yapı tanımlanmış olacak ve bu yapıda üç ayrı değişken tanımlanmış olacaktır. Eğer tek bir yapı çeşidi kullanılacaksa yapı için isim belirlemeye de gerek yoktur. Bu durumda değişkenler ardından ilan edildikleri yapıda oluşacaklardır. Alttaki örnek geçerli bir yapı ve değişken tanımlamasıdır.

struct{
    char school_name[15];
    short int number;
    char exam1;
    char exam2;
} gokhan_keskin, ahmet_ozturk, mehmet_sahin;

Bir yapı değişkeninin içindeki tekil elemanlara ise işlemler konusunda kısaca adı geçmiş olan nokta işlemi ile ulaşılır:

değişken_ismi.eleman_ismi

Örneğin gokhan_keskin değişkenindeki ilk sınav sonucuna erişmek için şu yapılacaktır:

gokhan_keskin.exam1

#include <stdio.h>

int main(void){
    struct student{
        char school_name[15];
        int number;
        int exam1;
        int exam2;
    };

    struct student gokhan_keskin;
    printf("School Name: ");
    scanf("%s",&gokhan_keskin.school_name);
    printf("Student Number: ");
    scanf("%d",&gokhan_keskin.number);
    printf("Exam 1: ");
    scanf("%d",&gokhan_keskin.exam1);
    printf("Exam 2: ");
    scanf("%d",&gokhan_keskin.exam2);
    printf("\n");

    printf("Gokhan Keskin\t%s\t",gokhan_keskin.school_name);
    printf("%d\n",gokhan_keskin.number);
    printf("Exam 1: %d\n",gokhan_keskin.exam1);
    printf("Exam 2: %d\n\n",gokhan_keskin.exam2);

    return 0;
}

Örneğin yukarıdaki alıştırmada "Gökhan Keskin" isimli öğrenciye ilişkin tüm bilgiler yapıdaki değişkenlere girilmiştir. Daha sonra da öğrencinin tüm bilgileri yazdırılmıştır.

İki yapıdan birisi diğerine doğrudan atanabilir. Diyelim ki Gökhan Keskin isimli öğrenciyle Ahmet Öztürk isimli öğrencinin tüm bilgileri, sınav sonuçlarına varana kadar, aynı olsun. Fakat bu öğrencinin numarası 1112 olsun. Bu durumda önce Gökhan Keskin'in bilgileri olduğu gibi Ahmet Öztürk için oluşturulan yapıya atanıp sonrasında numarası değiştirilebilir. Bunu yaparken her elemanı tek tek atamaya gerek yoktur.

#include <stdio.h>

int main(void){
    struct student{
        char school_name[15];
        int number;
        int exam1;
        int exam2;
    };

    struct student gokhan_keskin;
    printf("School Name: ");
    scanf("%s",&gokhan_keskin.school_name);
    printf("Student Number: ");
    scanf("%d",&gokhan_keskin.number);
    printf("Exam 1: ");
    scanf("%d",&gokhan_keskin.exam1);
    printf("Exam 2: ");
    scanf("%d",&gokhan_keskin.exam2);
    printf("\n");

    printf("Gokhan Keskin\t%s\t",gokhan_keskin.school_name);
    printf("%d\n",gokhan_keskin.number);
    printf("Exam 1: %d\n",gokhan_keskin.exam1);
    printf("Exam 2: %d\n\n",gokhan_keskin.exam2);

    struct student ahmet_ozturk;
    ahmet_ozturk=gokhan_keskin;
    ahmet_ozturk.number=1112;

    printf("Ahmet Ozturk\t%s\t",ahmet_ozturk.school_name);
    printf("%d\n",ahmet_ozturk.number);
    printf("Exam 1: %d\n",ahmet_ozturk.exam1);
    printf("Exam 2: %d\n\n",ahmet_ozturk.exam2);

    return 0;
}

Yukarıdaki örnekte önce Gökhan Keskin isimli öğrenciye ait bilgiler bir yapı içinde toplanmış daha sonra aynı veriler Ahmet Öztürk için oluşturulan yapıya atanmış ve sonrasında Ahmet Öztürk için oluşturulan yapıdaki numara elemanı değiştirilmiştir. Ahmet Öztürk'e ait yapıdaki elemanlar yazdırıldığında numara hariç tüm elemanların Gökhan Keskin'inki ile aynı olduğu görülecektir.

Değişkenlerden olduğu gibi yapılardan da diziler oluşturulabilir. Örneğin yukarıdaki örnekte öğretmen her öğrenci için bir yapı oluşturmaktansa yapıların içine öğrenci isimleri için de bir eleman ekleyerek öğrencilerin bilgilerini oluşturan bir yapı dizisi oluşturabilir. Yapının şekli tanımlandıktan sonra bir yapı dizisi oluşturmak normal dizi oluşturmaya çok benzerdir:

...
struct student{
    char student_name[20];
    char student_surname[20];
    char school_name[15];
    short int number;
    char exam1;
    char exam2;
};
struct student term2013[3];
...

Misalen yukarıda term2013 isimli student tipindeki yapıda 3 elemanlı bir dizi ilan edilmiştir. Şimdi bu öğretmenin üç öğrencisi için bilgileri girdiği ve bunları yazdırdığı bir örnek yapalım:

#include <stdio.h>

int main(void){
    struct student{
        char student_name[20];
        char student_surname[20];
        char school_name[15];
        int number;
        int exam1;
        int exam2;
    };

    struct student term2013[3];
    int i;

    for(i=0;i<3;i++){
        printf("Student Name: ");
        scanf("%s",&term2013[i].student_name);
        printf("Student Surname: ");
        scanf("%s",&term2013[i].student_surname);
        printf("School Name: ");
        scanf("%s",&term2013[i].school_name);
        printf("Student Number: ");
        scanf("%d",&term2013[i].number);
        printf("Exam 1: ");
        scanf("%d",&term2013[i].exam1);
        printf("Exam 2: ");
        scanf("%d",&term2013[i].exam2);
        printf("\n");
    }

    for(i=0;i<3;i++){
        printf("%s ",term2013[i].student_name);
        printf("%s ",term2013[i].student_surname);
        printf("%s ",term2013[i].school_name);
        printf("%d\n",term2013[i].number);
        printf("Exam 1: %d\n",term2013[i].exam1);
        printf("Exam 2: %d\n\n",term2013[i].exam2);
    }
    return 0;
}

Yukarıdaki örnekte görüldüğü gibi yapı dizisinin elemanlarına erişmek için yapı değişkeninin ismi indislenmiştir. Çünkü burada erişilmek istenen yapı dizisinin içindeki bir değişkendir. Diyelim ki ikinci öğrencinin isminin ilk harfine erişilmek istenmiş olsun. Bu defa hem yapı dizisinin ismi hem de yapının içindeki elemanın ismi indislenmelidir: "term2013[1].student_name[0]". Bu ifade karakter olarak yazdırılırsa karşımıza girilen ikinci öğrenci isminin ilk harfi çıkacaktır.


Yapı Elemanlarını Fonksiyona Argüman Olarak Verme

Yapının içerisindeki elemanlar normal birer değişken gibi görülebilir ve fonksiyonlara verilme şekilleri herhangi bir değişkeni fonksiyona vermekten farklı değildir. Bir elemanın adresini almak için ise en başa  & işareti konulmalıdır: "&gokhan_keskin.number"

Elemanın dizi olması durumunda ise elemana erişilirken parantezlerin kullanılmaması adresi almaya yetecektir: "gokhan_keskin.school_name"


Yapıları Tümüyle Fonksiyona Argüman Olarak Verme

Yapıları fonksiyonlara argüman olarak vermenin bir yolu fonksiyona değerleri vermektir. Bunun için kullanılacak fonksiyon şu formatta tanımlanmalıdır:

dönüş_tipi fonksiyon_ismi (struct yapı_tipi formal_parametre){
...
}

Örnekle daha iyi anlaşılacaktır:

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

struct student{
    char student_name[20];
    char school_name[15];
    int number;
    int exam1;
    int exam2;
};

int avarage(struct student x);

int main(void){
    struct student gokhan_keskin;
    strcpy(gokhan_keskin.student_name,"Gokhan Keskin");
    strcpy(gokhan_keskin.school_name,"ITU");
    gokhan_keskin.number=1111;
    gokhan_keskin.exam1=35;
    gokhan_keskin.exam2=55;
    int a=avarage(gokhan_keskin);
    printf("Avarage: %d\n",a);
    return 0;
}

int avarage(struct student x){
    printf("%s ",x.student_name);
    printf("%s ",x.school_name);
    printf("%d\n",x.number);
    printf("Exam 1: %d\n",x.exam1);
    printf("Exam 2: %d\n\n",x.exam2);
    return (x.exam1+x.exam2)/2;
}

Örnekte Gökhan Keskin isimli öğrencinin bilgilerini yazdıran ve sonrasında bu öğrencinin notlarının ortalamasına dönen bir fonksiyon tanımlanmıştır. Buradaki strcpy fonksiyonuna değinmek gerekirse bu bir dizgiye başka bir dizgiyi kopyalamaya yarayan bir fonksiyondur. Dizilere ilan edildikleri satır dışında, tek tek her elemanına erişilmeden atama yapılamadığı için kullanımı sık görülen bir fonksiyondur. string.h kütüphanesi içinde bu fonksiyon daha detaylı anlatılacaktır. Bu örnekte dikkat edilmesi gereken birkaç husus vardır:

Öncelikle fonksiyona argüman olarak verilen yapı tipiyle fonksiyon tanımlanırken kullanılan yapı tipi aynı olmalıdır. Misalen bu örnekte yapı tipi "student"dir. Eğer "student2" isimli bir yapı tipi oluşturulsaydı, bu yapı tipi de "student" yapı tipiyle birebir aynı elemanlara sahip olsaydı ve gokhan_keskin değişkeni "student2" yapı tipiyle oluşturulmuş olsaydı fonksiyon "student" yapı tipi için tanımlandığından çalışmayacaktı.

Dikkat edilmesi gereken bir başka husus da yapı tipinin global olarak tanımlanmış olmasıdır. Bu böyle olmalıdır, aksi takdirde fonksiyon yapı tipini göremeyecektir. Fonksiyonlarda kullanılacak yapı tipleri her zaman global olarak, yani tüm fonksiyonların dışında tanımlanmalıdır.

Yapıları tümüyle bir fonksiyona argüman olarak vermek adres kullanılarak da yapılabilir. Bunun için öncelikle yapılar için işaretçilerin kullanımına bakalım.

Bir yapı değişkeni için işaretçi tanımlamak da normal bir değişkendekinden farklı değildir:

struct yapı_değişkeni_ismi *işaretçi_ismi;

Bir yapı değişkeninin adresi & işlemi ile alınır. Örneğin aşağıdaki örnekte p işaretçisi gokhan_keskin yapı değişkeninin adresini alacaktır:

struct student *p;
p=&gokhan_keskin;

Daha önceden bahsi geçen ok işlemi yani -> ise bir işaretçide adresi tutulan yapı değişkenindeki tekil elemanlara erişmeye yarar. Misalen "p->number" ile Gökhan Keskin'in öğrenci numarasına erişilir.

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

struct student{
    char student_name[20];
    char school_name[15];
    int number;
    int exam1;
    int exam2;
};

int avarage(struct student *x);

int main(void){
    struct student gokhan_keskin;
    strcpy(gokhan_keskin.student_name,"Gokhan Keskin");
    strcpy(gokhan_keskin.school_name,"ITU");
    gokhan_keskin.number=1111;
    gokhan_keskin.exam1=35;
    gokhan_keskin.exam2=55;
    struct student *p;
    p=&gokhan_keskin;
    int a=avarage(p);
    printf("Avarage: %d\n",a);
    return 0;
}

int avarage(struct student *x){
    printf("%s ",x->student_name);
    printf("%s ",x->school_name);
    printf("%d\n",x->number);
    printf("Exam 1: %d\n",x->exam1);
    printf("Exam 2: %d\n\n",x->exam2);
    return (x->exam1+x->exam2)/2;
}

Yukarıda Gökhan Keskin'in bilgilerini yazdıran ve not ortalamasını hesaplayan örnek bu sefer işaretçi kullanılarak yapılmıştır. Unutulmaması gereken bir nokta da şudur; diğer değişkenlerde de olduğu gibi işaretçinin fonksiyona argüman olarak verilmesinden sonra fonksiyon içinde değişkenler üzerinde yapılan değişiklikler fonksiyon dışına da yansır.

Yapı dizilerini fonksiyonlara argüman olarak vermek ve yapı dizilerini kullanan fonksiyonlar tanımlamak da normal dizilerin fonksiyonlarda kullanılması ve tanımlanmasına benzer. Örneğin üç öğrenci için de not bilgilerini girmiş olan öğretmen, notlarının ortalamalarını hesaplatmak ve bunları çıktı olarak görmek istesin. Aşağıdaki örnek bunu sağlayacaktır:

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

struct student{
    char student_name[20];
    char school_name[15];
    int number;
    int exam1;
    int exam2;
};

void avarage(struct student x[]);

int main(void){
    struct student term2013[3];

    strcpy(term2013[0].student_name,"Gokhan Keskin");
    strcpy(term2013[0].school_name,"ITU");
    term2013[0].number=1111;
    term2013[0].exam1=35;
    term2013[0].exam2=55;

    strcpy(term2013[1].student_name,"Ahmet Ozturk");
    strcpy(term2013[1].school_name,"ITU");
    term2013[1].number=1112;
    term2013[1].exam1=45;
    term2013[1].exam2=70;

    strcpy(term2013[2].student_name,"Mehmet Sahin");
    strcpy(term2013[2].school_name,"Yeditepe");
    term2013[2].number=5050;
    term2013[2].exam1=52;
    term2013[2].exam2=80;

    avarage(term2013);
    return 0;
}

void avarage(struct student x[]){
    char i;
    for(i=0;i<3;i++){
        printf("%s\n",x[i].student_name);
        printf("%d\n\n",(x[i].exam1+x[i].exam2)/2);
    }
}





Fonksiyonlar <<<<< Temel C >>>>> Birlikler