Volkan Atasever

mühendislik, edebiyat, retro computer, yazılım, şiir, nümismatik, bilişim, podcast ve daha bir çok yazılarım...

Yapılar, Birlikler ve Bit Alanları

clock Mart 30, 2006 08:13 by author Volkan Atasever

Özet: Yapı tanımı ,örnekleri ve yapı işaretçileri. Yapı’ların fonksiyonlara parametre olarak gönderilmesi ve yapı döndüren fonksiyonlar. Birlik tanımı ve örnekleri. Yapı ile Birlik’ların iç içe kullanımı. Bit Alanları’nın açıklanması ve örnekleri.

Bu makalemde yapı, birlik ve bit alanları ile ilgili tanım,tanımlamalar ve örnek uygulamalar üzerinde duracağım. C dilinde int, float, char vb. ilkel veri tiplerimiz olduğu gibi bir çeşit kendi veri tipimizi tanımlama olanağımız da vardır. Bu tanımlamayı da struct anahtar sözcüğümüz ile gerçekleştirebiliyoruz. Küçük bir örnek vermek gerekirse:

struct ornek{
     int a;
    char b;
    float c;
};

Şeklinde olacaktır.

Yapımızın yapısını incelersek struct anahtar sözcüğü ve örnek adında bir yapı etiketi kullandık. Her yapı ‘;’ ile bitmek zorundadır. Yapıların boyutu ise içindeki veri tiplerinin toplam boyu kadardır. int,float ve char veri tiplerimizin sırayla 2,4 ve 1 byte büyüklüğünde bellekte yer kapladıklarını düşünelim (Farklı derleyiciler de farklı temel veri tipi boyutuna rastlamak mümkün), o zaman struct örnek adındaki veri tipimizin toplam boyutu 7 byte olacaktır. Not: Alignment kavramı göz ardı edilerek 7 byte olacağı farzedilmiştir. Eğer alignment parametresi ile programımızı derlersek bir hizalama olacağı için göz önüne aldığımız sistemde 7byte yerine 8 byte yer kaplayacaktır. Alignment gerçekten programımızı hızlandıran bir seçenek olarak karşımıza çıkar ve CPU nun daha az yorulmasını sağlar. Eğer yapımızı bir veri tipi olarak düşünürsek fonksiyonlara parametre olarak göndermek ve geri dönüş değeri kullanmak bizim için daha da kolay bir hal alacaktır. Yapıların fonksiyonlarla kullanılmasına ve genel kullanımına ilişkin bir senaryo oluşturalım. Senaryomuz, iki tane farklı yapının farklı bellek bölgelerine kopyalanması olacaktır ve bunu bir fonksiyon yardmıyla gerçekleyecektir.
İlk önce yapı tasarımımızı oluşturalım:

typedef struct{
    char ad[15],
    soyad[15];
    int borc;
}kadir;

Burada typedef anahtar kelimesi sayesinde uygulamamızda veri tipimizi kadir adında kullanabilmekteyiz. Yapımızın boyu 32 byte’dır. Kullandığımız uygulamalarda direk hesaplamak yerine sizeof(kadir) veya sizeof(struct etiket_ismi) şeklinde de öğrenebilriz.
Fonksiyonumuz ise:

kadir *kopy(kadir *al){
    rewind(stdin);
    puts("String gir");
    gets(al->ad);
    puts("2. String gir");
    gets(al->soyad);
    puts("rakam gir");
    scanf("%d",&al->borc);
    return al;
}


Şeklinde olsun.Burada kopy adındaki fonksiyonumu parametre olarak kadir tipinden bir adres beklemektedir. kadir isimli veri tipimizi de typedef ile yapımız üzerinde tanımladık. Fonksiyonumuzun geri dönüş değeri ise yine kadir tipinden bir adres olacaktır. rewind(stdin) ile standart input’umuzu geriye sardık. Ve puts fonksiyonları ile bir adres yazdırdık. Dikkat edilmesi gereken yer ise gets fonksiyonumuz parametre olarak yolladığımız kadir tipinden al adında değişkenin elemanlarına ‘->’ işaretiyle ulaşıyoruz. Eğer işaretçi üstünden değil de direkt değer türünden oluşturmuş olsaydık ‘.’ İşaretiyle elemanlarımıza ulaşabilirdik(Örnek: gets(al.ad); ) . scanf fonksiyonumuzun ise geleneksel olarak okuttuğumuz temel veri tiplerinden farklı bir yazım olarak sadece &borc olması gereken yerde &al->borc şeklinde yazılmıştır. Son olarak da kadir tipinden al adresini döndürüyoruz. main fonksiyonumuz ise şöyle olmalıdır:

int main(void)
{
    kadir *x1;
    kadir *x2;
    clrscr();
    x1=(kadir *)calloc(sizeof(kadir),5);
    x2=(kadir *)calloc(sizeof(kadir),5);
    for(int j=0;j<5;++j) {
         *(x2+j)=*(kopy(x1+j));
    }
    for(int r=0;r<5;++r){
        puts((x2+r)->ad);
        puts((x2+r)->soyad);
        printf("%d",(x2+r)->borc);
    }
    getch();
    return 0;
}


kadir tipinden iki adet işaretçi tanımladık ve bunlara calloc fonksiyonu ile sizeof(kadir)*5 kadar yer aldık yani toplamda 5’er adet yapımız için yer açtık. Eğer sizeof() işlecini kullanmasaydık ‘(kadir *)calloc(32,5);’ da diyebilirdik. Ardından for döngümüzü 5 kere dönecek şekilde kurduk ve içinde kopy fonksiyonumuzu çalıştırdık. x1+j adresini fonksiyona parametre olarak verdik(x1+0,x1+1,…x1+4 olarak parametreler gidecektir) ve geri dönüş değerimizi işaretçi olduğu için onun * operandıyla değerini *(x2+j) nin gösterdiği adresinin değerine yerleştirmiş olduk.

Not: Yapı işaretçilerinin 1 adres ileri gitmesi yapının boyu kadar olur. Diyelim yapımız 64 byte biz adresimizi bir arttırdığımızda 64 byte ileri gider. Veya 4 adres ileri gidersek 64*4 kadar yani 256 byte kadar ilerlemiş olur.
Bu şekilde x1 adresimize bilgileri kopy fonksiyonuyla okuyup yine sırasıyla x2 ye yerleştirmiş olduk. Adresler hakkında ki bilgi ve RAM’lerdeki yerleşimleri için alttaki resmi inceleyebilrisiniz.

İkinci for döngümüzde ise x2+r diyerek sırasıyla 5 adet yapımızın içinde olan bilgileri konsola yazdırdık. union yani birliklerin tanımları ve kullanılması da struct’larla benzerlik gösterirler. union’ların bize getirmiş olduğu en büyük kazanç aynı bellek adresini değişik veri tipleri için kullanabilmemizdir. Basit bir union tanımlamak gerekirse:

union deneme{
    int a;
    char b;
    int dizi[3];
};

Birliğimizde bir tam sayı değişkeni, bir karakter değişkeni ve bir de tam sayı dizisi bulunmaktadır. union deneme isimli birliğimizin boyu en büyük elemanının boyu kadardır burada da 3 elemanlı tam sayı dizimiz 6 byte olduğu için toplam boyut da 6 byte dır. Yapı ve birliklerin iç içe kullanımı ile ilgili bir örnek düşünürsek:

struct AYIR{
    char cIlk,
    cSon;
};
typedef union kisa{
    short int shSayi;
    struct AYIR AKrktr;
}sayim;

Öncelikle birliğimizi inceleyecek olursak içinde kısa bir tam sayı ve struct AYIR tipinden AKrktr isimli bir değişken tanımlanmış bulunuyor. Yapımızda ise cIlk ve cSon adında iki karakter değişkeni barındırmaktadır. İsterseniz bu tasarımımızın pratikte nasıl kullanılabileceğini düşünebiliriz. main() fonksiyonumuzu oluşturalım:

int main(){
    sayim saTest;
    saTest.shSayi=19527;
    printf("shSayi:%d cIlk: %c\n
    cSon:%c\n",saTest.shSayi,
    saTest.AKrktr.cIlk,
    saTest.AKrktr.cSon);
    printf("int: %d short int: %d char: %d",
    sizeof(int),sizeof(short int),sizeof(char));
    getch();
    return 0;
}

Burada typedef ile ile belirttiğimiz kendi oluşturduğumuz türden bir değişken tanımlıyoruz aynı zamanda bu birliğimizi barındırmaktadır. Ardından değişken üzerinden birliğimizin elemanlarından olan shSayi adlı değişkene 19527 sayısını atıyoruz. Çalıştırdığımızda ise çıktımız:

Kısa tamsayının 2 byte, karakterin ise 1 byte yer kapladığını çıktıdan görebiliyoruz. Birliklerin aynı bölgeyi kullandığını ve bu bölgeninde bizim yapımıza tekabül ettiğini unutmamız lazım. Yapımızda ise kendi içindeki 1 byte lık değişkenler sayesinde 19527 sayısının ilk 8 bitini cIlk ikinci 8 bitini ise cSon karşılamaktadır. İlk 8 bitin karşılığı G ve ikinci 8 bitin karşılığı ise L’dir. En basit tanımıyla herhangi bir karakterin bitlerini elde etmek için kullanacağımız ve bit düzeyinde işlem yapacağımız silahımız bit alanlarıdır. Alt seviyeli programlamada vazgeçilmezler arasında yer almaktadır düşünüyorum da sıradan bir com port’u programlamak ve bir protokol meydana getirip bit bit ele alınmadada faydalı olabilir. Biz şu anda temelini irdelemek için basit bir bitlere ayrıştırma programı tasarlayalım.

struct bitler {
    int iIlkBit: 1;
    int iIknci: 1;
    int iUcncu: 1;
    int iDrdncu: 1;
    int iBsnci: 1;
    int iAltnci: 1;
    int iYednci: 1;
    int iSonBit: 1;
};
union ikili {
    char ch;
    struct bitler test;
}ayir;

Bir önceki verdiğimiz örnek ile yapımızın ve birliğimizin uyumu aynıdır. Tek fark ise yapımızın artık sıradan bir yapı olmayıp bit alanı diye isimlendirilmesidir. Basit manada : işlecinin hemen ardından o değişkene kaç bit yer ayırdığımızı belirleyebiliriz. Şu anda bit alanımız 8 bitlik veriyi saklayabilme kapasitesine sahiptir. Eğer birliğimizde bir 4 byte’lık tam sayı tanımlamış olsaydık, ilk iki bitine ve son bitine ulaşmak bizim için önemli olsaydı bit alanımızda şöyle bir oynama yapabilirdik.
Not: 4 byte 32 bittir.

struct tamsayı{
    int birinci:1;
    int ikinci:1;
    :29;
    int son:1;
};

‘:’ işlecinden sonra yazdığımız 29 ilk iki bitten sonraki 29 biti önemsememiz gerektiğini belirtmektedir. Son olarak örneğimize dönecek olursak bitlerine ayırmak için kullanacağımız main() fonksiyonumuz şöyle olacaktır:

 

int main(void)
{
    printf("Bir Karakter Giriniz\n");
    scanf("%d",&ayir.ch);
    printf("\nKarakterin Bitlerine Ayrilmis Sekli:\n");
    if(ayir.test.iSonBit)
         printf("1");
    else
        printf("0");
    if(ayir.test.iYednci)
        printf("1");
    else
        printf("0");
    if(ayir.test.iAltnci)
        printf("1");
    else
        printf("0");
    if(ayir.test.iBsnci)
        printf("1");
    else
        printf("0");
    if(ayir.test.iDrdncu)
        printf("1");
    else
        printf("0");
    if(ayir.test.iUcncu)
        printf("1");
    else
        printf("0");
    if(ayir.test.iIknci)
        printf("1");
    else
        printf("0");
    if(ayir.test.iIlkBit)
        printf("1");
    else
         printf("0");

     getch();
    return 0;
}

 

Gördüğünüz gibi 99 sayısının bitlerine ayrılmış şekli çıktıda verilmiştir basit bir if else mantığıyla çok kolay bir şekilde elde edebildik. Unutmamanız gereken bit alanları alt seviyeli programlarda gerçekten çok faydalı işlere imza atmaktadır.

Sorularınızı ve yorumlarınızı volkan@volkanatasever.com adresine yollayabilirsiniz.
Yazan: Volkan Atasever



Basit Dosya İşlemleri - 3

clock Kasım 25, 2005 08:09 by author Volkan Atasever

Basit dosya işlemleri ve Basit Dosya İşlemleri – 2 adlı makalelerimin devamı olan bu makalemde sizlere dosyalar üstünde yapabileceğimiz diğer incelikleri göstereceğim. Mesela soyadına göre arama yapma veya dosyamızdaki bir kaydı değiştirme gibi. Dosyamız önceki makalede oluşturulmuş tu, buradaki anlatacaklarım da oraya bağımlı olarak devam edecektir.

Kayıt numarasına göre arama vb. uygulamalar karışıklığa yol açtığı için alt kriterlerde arama yapma ihtiyacı doğmuştur. Bir kütüphane uygulaması düşünelim. Kitabın sadece yazarını biliyoruz ve bize o yazara ait kitaplar lazım. Veya kitap adını biliyoruz, yazar adı lazım. Hepsi kod yazma sanatının incelikleridir. Yine de hepsi bir oluşumu işaret etmektedir, yani dosyayı.

Bir program yaparken de en memnuniyetsiz müşterinin ihtiyaçlarını karşılamak bizim için bir çıtadır (Bill Gates de sırlarını açırken buna benzer bir uygulamaya değinmiştir).

Şu anda ki temel ihtiyaçlarımız bunlardır. O zaman şimdi başlık (header) dosyalarımızı ve define ile belirttiğimiz ifadeleri tekrar hatırlayalım.  

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

#define MAX1 15
#define MAX2 15
#define MAX3 12
#define MAX4 45
#define MAX5 10
#define MAX6

//Yapımızı ise şöyleydi :

typedef struct kayit {
    char soyad[MAX1];
    char ad[MAX2];
    char tel[MAX3];
    char adres[MAX4];
    char no[MAX5];
    char not[MAX6];
}muvk;

Yapımız bir avukatın minimum ihtiyaçlarını karşılayacak şekilde tasarlanmıştır. İsterseniz ilk önce dosyamızda kriterlere göre arama yapan fonksiyonumuzu yazalım. Tek bir fonksiyon bize bütün kriterleri gerçekleştirmemizi sağlayacak minimum bilgiyi içermektedir.

void soyad()
{
    clrscr();
    FILE *fp;
    muvk *kayit;
    int i,kadir;
    char ara[15];

    do{
       kayit=(muvk *)calloc(sizeof(muvk),1);
    }while(kayit==NULL);

    fp=fopen("Muvekkil.vol","r");
    fseek(fp,0,2);
    kadir=ftell(fp)/sizeof(muvk);
    puts("Aranan kisinin Soyadi:");
    gets(ara);

    for(i=0;i kadir;++i){
       fseek(fp,i*103,0);
       fread(kayit->soyad,MAX1,1,fp);
       if(strcmp(ara,kayit->soyad)==0){
          rewind(fp);
          fseek(fp,i*103,0);
          fread(kayit->soyad,MAX1,1,fp);
          fread(kayit->ad,MAX2,1,fp);
          fread(kayit->tel,MAX3,1,fp);
          fread(kayit->adres,MAX4,1,fp);
          fread(kayit->dogum,MAX5,1,fp);
          fread(kayit->not,MAX6,1,fp);
          puts("Soyadi:");
          puts(kayit->soyad);
          puts("Adi:");
          puts(kayit->ad);
          puts("Telefonu:");
          puts(kayit->tel);
          puts("Adresi:");
          puts(kayit->adres);
          puts("Dogum Tarihi:");
          puts(kayit->dogum);
          puts("Not Ortalamasi:");
          puts(kayit->not);
       }
    }

    getch();
    fclose(fp);
    free(kayit);
}

Burada dikkat edilmesi gereken en önemli noktalardan biri, kadir değişkenine dosyanın hangi konumundaysak muvk veritipinin büyüklüğünü bölmüş olup kaç kayıt olduğunu bulmamızdır.

For döngümüzde her seferinde o anki kayıt açılmaktadır. For döngümüzün içinde kaçıncı kayıtsa, fseek hazır fonksiyonuyla o kısma konumlandırıyoruz. Sadece soyad kısmını okuyoruz. Hemen ardında if deyimimiz işletiliyor. strcmp() fonksiyonu if deyiminin vermiş olduğu müstesna kolaylıklardan ötürü bizim programımızla çok mükemmel bir şekilde işlemektedir.

İsterseniz strcmp() fonksiyonumuzu inceleyelim: strcmp() fonksiyonu, iki stringi karşılaştırıp bize bir tamsayı döndürür. (printf() ve scanf() fonksiyonlarının da bize geri dönüş değeri açısından sağladıkları faydaları unutmamak gerekir. Misal scanf() le klavyeden aldığımız herhangi bir basımın sayı veya karakter olduğunu bulabiliriz). strcmp() fonksiyonunun döndürdüğü sayıların anlamları şöyledir:

Değer Açıklaması
Sıfırdan Küçük İlk string, ikinci string’den küçük
Sıfır İlk string, ikinci stringe eşit
Sıfırdan Büyük İlk string, ikinci string’den büyük

Bizim uygulamamızda da sıfıra eşit mi diye kontrol yapılıyor. Bu eşitlik sağlanırsa if deyimi icra ediliyor yani girdiğimiz karakter katarı dosyadan okuduğumuz karakter katarına eşit çıkmış oluyor ve aradığımız kritere uygun kayıtlar listeleniyor. İf deyimi için de kullandığımız rewind(fp) fonksiyonumuz dosyanın geri sarılmasını ifade edip, o an ki döngüdeki i değerinin saklanmasından faydalanılarak yapılmış bir eylemdir. Kaçıncı kayıtta koşul sağlanmışsa, tekrar fseek() fonksiyonu bize kolaylık sağlamaktadır. Dosyadaki herhangi bir kaydı düzeltmek ise yine bizim vazgeçilmezlerimizden ve temel işlemlerden biridir. Düzelteceğimiz kayda gidip bu işlemleri yapmak bizi oldukça memnun edecektir. Her türlü işlemi, bu fonksiyonları kullanarak yerine getirebilirsiniz.

void change()
{
    FILE *fp;
    muvk *kayit;
    int kadir,number;

    clrscr();

    do{
       kayit=(muvk *)calloc(sizeof(muvk),1);
    }while(kayit==NULL);

    fp=fopen("Muvekkil.vol","r+");
    fseek(fp,0,2);
    kadir=ftell(fp)/sizeof(kayit);
    puts("Kayıt No Giriniz:");
    scanf("%d",number);
    fseek(fp,&number*103,0);
    fflush(stdin);
    puts("Yeni Soyad:");
    gets(kayit->soyad);
    puts("Yeni Ad:");
    gets(kayit->ad);
    puts("Yeni Telefon:");
    gets(kayit->tel);
    puts("Yeni Adres:");
    gets(kayit->adres);
    puts("Yeni Dogum Tarihi:");
    gets(kayit->dogum);
    puts("Yeni Not Ortalamasi:");
    gets(kayit->not);
    fwrite(kayit,sizeof(muvk),1,fp);
    fclose(fp);
    getch();
    free(kayit);
}

Bu fonksiyonumuzda ise kayıt no’suna göre bir arama yapılmaktadır. Kayıt no’su alınarak o kayda gidilip yeniden o bölgede değiştirilme yapılmıştır. Yazıda kullanılan fonksiyonların prototipleri ve açıklamaları:

Fonksiyon Adı Açıklaması
puts #include <stdio.h>
int puts(const char *str);
gets #include <stdio.h>
char gets(char *str) ;
ftell #include <stdio.h>
long int ftell(FILE *stream);
fseek #include <stdio.h>
int fseek(FILE *stream, long int offset,int origin);
fclose #include <stdio.h>
int fclose(FOLE *stream);
Stream ile ilişkili dosyayı kapatır ve tamponu temizler.
free #include <stdlib.h>
void free(void *ptr);
ptr’nin gösterdiği bellek bölgesini serbest bırakır ve bu bölgenin sonraki tahsisatlara hazır hale gelmesini sağlar.
calloc #include <stdlib.h>
void *calloc(size_t num, size_t size);
(num * size ) büyüklüğünde bellek tahsisatı yapar. Özetle size büyüklüğünde, num sayıda nesne içeren bir dizi için yeterli bellek tahsisatını yapar.

Bu makale veya diğer makalelerim hakkında görüşlerinizi ekleyebileceklerinizi veya yorumlarınızı volkan@volkanatasever.com adresine mail olarak ulaştırabilirsiniz.



Basit Dosya İşlemleri - 2

clock Kasım 1, 2005 08:03 by author Volkan Atasever

Basit dosya işlemleri ve Basit Dosya İşlemleri – 2 adlı makalelerimin devamı olan bu makalemde sizlere dosyalar üstünde yapabileceğimiz diğer incelikleri göstereceğim. Mesela soyadına göre arama yapma veya dosyamızdaki bir kaydı değiştirme gibi. Dosyamız önceki makalede oluşturulmuş tu, buradaki anlatacaklarım da oraya bağımlı olarak devam edecektir.

Kayıt numarasına göre arama vb. uygulamalar karışıklığa yol açtığı için alt kriterlerde arama yapma ihtiyacı doğmuştur. Bir kütüphane uygulaması düşünelim. Kitabın sadece yazarını biliyoruz ve bize o yazara ait kitaplar lazım. Veya kitap adını biliyoruz, yazar adı lazım. Hepsi kod yazma sanatının incelikleridir. Yine de hepsi bir oluşumu işaret etmektedir, yani dosyayı.

Bir program yaparken de en memnuniyetsiz müşterinin ihtiyaçlarını karşılamak bizim için bir çıtadır (Bill Gates de sırlarını açırken buna benzer bir uygulamaya değinmiştir).

Şu anda ki temel ihtiyaçlarımız bunlardır. O zaman şimdi başlık (header) dosyalarımızı ve define ile belirttiğimiz ifadeleri tekrar hatırlayalım.  

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

#define MAX1 15
#define MAX2 15
#define MAX3 12
#define MAX4 45
#define MAX5 10
#define MAX6

//Yapımızı ise şöyleydi :

typedef struct kayit {
    char soyad[MAX1];
    char ad[MAX2];
    char tel[MAX3];
    char adres[MAX4];
    char no[MAX5];
    char not[MAX6];
}muvk;

Yapımız bir avukatın minimum ihtiyaçlarını karşılayacak şekilde tasarlanmıştır. İsterseniz ilk önce dosyamızda kriterlere göre arama yapan fonksiyonumuzu yazalım. Tek bir fonksiyon bize bütün kriterleri gerçekleştirmemizi sağlayacak minimum bilgiyi içermektedir.

void soyad()
{
    clrscr();
    FILE *fp;
    muvk *kayit;
    int i,kadir;
    char ara[15];

    do{
       kayit=(muvk *)calloc(sizeof(muvk),1);
    }while(kayit==NULL);

    fp=fopen("Muvekkil.vol","r");
    fseek(fp,0,2);
    kadir=ftell(fp)/sizeof(muvk);
    puts("Aranan kisinin Soyadi:");
    gets(ara);

    for(i=0;i kadir;++i){
       fseek(fp,i*103,0);
       fread(kayit->soyad,MAX1,1,fp);
       if(strcmp(ara,kayit->soyad)==0){
          rewind(fp);
          fseek(fp,i*103,0);
          fread(kayit->soyad,MAX1,1,fp);
          fread(kayit->ad,MAX2,1,fp);
          fread(kayit->tel,MAX3,1,fp);
          fread(kayit->adres,MAX4,1,fp);
          fread(kayit->dogum,MAX5,1,fp);
          fread(kayit->not,MAX6,1,fp);
          puts("Soyadi:");
          puts(kayit->soyad);
          puts("Adi:");
          puts(kayit->ad);
          puts("Telefonu:");
          puts(kayit->tel);
          puts("Adresi:");
          puts(kayit->adres);
          puts("Dogum Tarihi:");
          puts(kayit->dogum);
          puts("Not Ortalamasi:");
          puts(kayit->not);
       }
    }

    getch();
    fclose(fp);
    free(kayit);
}

Burada dikkat edilmesi gereken en önemli noktalardan biri, kadir değişkenine dosyanın hangi konumundaysak muvk veritipinin büyüklüğünü bölmüş olup kaç kayıt olduğunu bulmamızdır.

For döngümüzde her seferinde o anki kayıt açılmaktadır. For döngümüzün içinde kaçıncı kayıtsa, fseek hazır fonksiyonuyla o kısma konumlandırıyoruz. Sadece soyad kısmını okuyoruz. Hemen ardında if deyimimiz işletiliyor. strcmp() fonksiyonu if deyiminin vermiş olduğu müstesna kolaylıklardan ötürü bizim programımızla çok mükemmel bir şekilde işlemektedir.

İsterseniz strcmp() fonksiyonumuzu inceleyelim: strcmp() fonksiyonu, iki stringi karşılaştırıp bize bir tamsayı döndürür. (printf() ve scanf() fonksiyonlarının da bize geri dönüş değeri açısından sağladıkları faydaları unutmamak gerekir. Misal scanf() le klavyeden aldığımız herhangi bir basımın sayı veya karakter olduğunu bulabiliriz). strcmp() fonksiyonunun döndürdüğü sayıların anlamları şöyledir:

Değer Açıklaması
Sıfırdan Küçük İlk string, ikinci string’den küçük
Sıfır İlk string, ikinci stringe eşit
Sıfırdan Büyük İlk string, ikinci string’den büyük

Bizim uygulamamızda da sıfıra eşit mi diye kontrol yapılıyor. Bu eşitlik sağlanırsa if deyimi icra ediliyor yani girdiğimiz karakter katarı dosyadan okuduğumuz karakter katarına eşit çıkmış oluyor ve aradığımız kritere uygun kayıtlar listeleniyor. İf deyimi için de kullandığımız rewind(fp) fonksiyonumuz dosyanın geri sarılmasını ifade edip, o an ki döngüdeki i değerinin saklanmasından faydalanılarak yapılmış bir eylemdir. Kaçıncı kayıtta koşul sağlanmışsa, tekrar fseek() fonksiyonu bize kolaylık sağlamaktadır. Dosyadaki herhangi bir kaydı düzeltmek ise yine bizim vazgeçilmezlerimizden ve temel işlemlerden biridir. Düzelteceğimiz kayda gidip bu işlemleri yapmak bizi oldukça memnun edecektir. Her türlü işlemi, bu fonksiyonları kullanarak yerine getirebilirsiniz.

void change()
{
    FILE *fp;
    muvk *kayit;
    int kadir,number;

    clrscr();

    do{
       kayit=(muvk *)calloc(sizeof(muvk),1);
    }while(kayit==NULL);

    fp=fopen("Muvekkil.vol","r+");
    fseek(fp,0,2);
    kadir=ftell(fp)/sizeof(kayit);
    puts("Kayıt No Giriniz:");
    scanf("%d",number);
    fseek(fp,&number*103,0);
    fflush(stdin);
    puts("Yeni Soyad:");
    gets(kayit->soyad);
    puts("Yeni Ad:");
    gets(kayit->ad);
    puts("Yeni Telefon:");
    gets(kayit->tel);
    puts("Yeni Adres:");
    gets(kayit->adres);
    puts("Yeni Dogum Tarihi:");
    gets(kayit->dogum);
    puts("Yeni Not Ortalamasi:");
    gets(kayit->not);
    fwrite(kayit,sizeof(muvk),1,fp);
    fclose(fp);
    getch();
    free(kayit);
}

Bu fonksiyonumuzda ise kayıt no’suna göre bir arama yapılmaktadır. Kayıt no’su alınarak o kayda gidilip yeniden o bölgede değiştirilme yapılmıştır. Yazıda kullanılan fonksiyonların prototipleri ve açıklamaları:

Fonksiyon Adı Açıklaması
puts #include <stdio.h>
int puts(const char *str);
gets #include <stdio.h>
char gets(char *str) ;
ftell #include <stdio.h>
long int ftell(FILE *stream);
fseek #include <stdio.h>
int fseek(FILE *stream, long int offset,int origin);
fclose #include <stdio.h>
int fclose(FOLE *stream);
Stream ile ilişkili dosyayı kapatır ve tamponu temizler.
free #include <stdlib.h>
void free(void *ptr);
ptr’nin gösterdiği bellek bölgesini serbest bırakır ve bu bölgenin sonraki tahsisatlara hazır hale gelmesini sağlar.
calloc #include <stdlib.h>
void *calloc(size_t num, size_t size);
(num * size ) büyüklüğünde bellek tahsisatı yapar. Özetle size büyüklüğünde, num sayıda nesne içeren bir dizi için yeterli bellek tahsisatını yapar.

Bu makale veya diğer makalelerim hakkında görüşlerinizi ekleyebileceklerinizi veya yorumlarınızı inhoftec@gmail.com adresine mail olarak ulaştırabilirsiniz.



Basit Dosya İşlemleri - 1

clock Kasım 1, 2005 08:01 by author Volkan Atasever

Bu ilk makalemde, size "C" de dosya oluşturup, onun üstünde nasıl işlem yapacağımızı anlatacağım. Yalnız bunu yaparken, bir konu üzerinden gideceğime, kendi yazmış olduğum basit örneklerden yaralanacağım. Benim de "C" öğrenirken en muzdarip olduğum şey, salt bilgiden çok, uygulamaya yönelik yazıların kısıtlı olmasıydı. Bu makale sonunda "C"nin, bu gerekli ve olmazsa olmazlarından, dosya işlemleri, hakkında bilgiye sahip olacaksınız. Bu yazımda kullanacağım program, ( her programıma özgün bir isim vermek adetimdir) aspecden V 0.9 isimli küçük bir sözlük programı olacaktır.Tabi ki bu sözlük programından daha güzel şeyler çıkartmak ise size kalmış bir olaydır. (Yaşasın GNU! ). İlk önce, programımızın taslağını oluşturalım. Bu sözlük programında, bize lazım olan kelimelerimizi eklemek ve onları istediğimiz zaman çağırıp, istediğimiz sözcüğü bulmak, aynı zamanda basit bir database mantığı var. Bunun için kendi yarattığımız iki fonksiyon kullanacağız: ekle() ve oku().Main’inimiz ve menünün düzeneği, kendi yaratıcılığımıza kalmış. Önce, ekle fonksiyonumuzu yazıp, yeni bir dosya oluşturalım. Yarattığımız dosya, sözcüğümüzün adını taşısın ve içinde sözcüğümüzün anlamını barındırsın. ekle fonksiyonumuz :

void ekle(void)
{
    clrscr(); 
    FILE *fp;
    char sozcuk[maks], aciklama[maks];
   
    fflush(stdin);
    printf("eklenecek sözcüğü giriniz\n");
    gets(sozcuk);
    fp=fopen(sozcuk,"wb");
    printf("aciklamasini giriniz\n");
    gets(aciklama);
    fwrite(&aciklama,sizeof(aciklama),1,fp);
    fclose(fp);
}

Not: Eğer Windows’dan farklı olarak Linux İşletim sistemini veya Dev-C++ tarzı bir derleyici kullanıyorsanız, içlerinde conio.h başlık dosyasını barındarmadığı için, Windows altında, system("cls") Linux altında system("clear") kullanınız. Öncelikle FILE isimli bir veri yapısı kullanıyoruz ve fp isimli bir işaretçi ile belirtiyoruz.Burada fp yerine, istediğiniz ismi kullanabilirsiniz. FILE stdio.h başlık dosyası içinde bildirilmiştir. FILE’ın içinde, dosyayla ilgili bazı bilgiler tanımlanmışdır ; dosyanın modu , tampon belleğin boyu vb. . "Sozcuk ve aciklama" adinda iki dizi tanımlıyoruz. Bunlar karakter tipinde diziler. "Sozcuk" dosyamızın ismi olacak ; "aciklama" ise yaratacağımız kelimenin anlamını oluşturacak. fflush(stdin) ‘i kullanmamızın sebebi ise, tampon belleği boşaltmak. Eğer kullanmazsak bazı problemlerle karşılaşabiliriz. En kötü ihtimal, gets(sozcuk) fonksiyonumuz işlemeyecektir, fflush(stdin) kullanmak istemiyorsanız, sahte bir gets de kullanabilirsiniz (tampon belleği boşaltmak için). O zaman sorunu halledebiliriz ama bilinçli bir davranışdan çok bir kaçış yolu olarak kullanılabilir. Gets fonksiyonu ise klavyeden girilen karakterlerin, hepsini diziye yerleştirir(dikkat edilmesi gereken husus, dizin uzunluğundan daha fazla karakter girişinede izin verilmektedir. Eğer böyle bir durumla karşılaşılırsa, dizi taşması olup, beklenmeyen hatalarla karşılaşabilir. Burada bir sözlük programı yaptığımız için, tek kelimelerle çalıştığımız farzedilmiştir). fp=fopen(sozcuk,"wb") ; burada sozcuk dizinine girmiş olduğumuz sözcüğün ismine sahip olan bir dosya, oluşturulmaktadır. wb ifademiz dosyayı, yoktan var etmemizi sağlar. Bu moda açılan bir dosyaya, sadece yazım yapılır, okuma yapılamaz. Şimdi, gets(aciklama) ile yukarıda da açıklandığı gibi klavyeden karakter alıyoruz. fwrite(&aciklama,sizeof(aciklama),1,fp) ; bellekte bulunan bilgiyi dosyaya yazmak için kullanmış, olduğumuz fonksiyondur. Genel kullanımı şöyledir : fwrite(&x, sizeof(int),1,fp); x değişkeninin içerisindeki sayı, fp ile belirtilen dosyaya yazılmaktadır. Bizim program parçacığımızda, fwrite’ın yaptığı iş, özetle aciklama isimli değişkeni fp nin gösterdiği dosyaya, yazmaktır. Açtığımız her dosyayı, kapatmak zorunda olduğumuz için fclose(fp) fonksiyonunu kullanmaktayız( açılan her dosya kapatılmak zorundadır yoksa işletim sisteminin özellikleriyle ilgili hatalara sebebiyet verebilir). Evet, ekle() fonksiyonumuz sayesinde, baştan varolmaya bir dosya oluşturup, içine de sözcüğümüzün anlamını aktardık. Sıra, girdiğimiz sözcüğü(dosyayı) okumaya yarayan bizim oluşturacağımız oku() fonksiyonunda:

void oku(void)
{
    clrscr();
    FILE *fp;
    char sozcuk[maks], aciklama[maks];
   
    fflush(stdin);
    printf("anlamini ogrenmek istediginiz sozcugu giriniz\n");
    gets(sozcuk);
    fp=fopen(sozcuk,"rbt");
    printf("girdiginiz sozcugun anlami\n");
    fread(&aciklama,sizeof(aciklama),1,fp);
    printf(" %s",aciklama);
    fclose(fp);
}  


Sanırım bu fonksiyonu derinlemesine anlatamama gerek yok. Gördüğünüz üzere, küçük bir iki değişiklikle, oluşturduğumuz dosyayı okutabiliyoruz. İlk göze çarpan değişiklik "rbt" kavramı. Burada dosyayı okumak için açıyoruz. fread() de fwrite() ‘ın kardeşi diyebiliriz, aynı kalıp. Açtığımız dosyayı kapatmayı unutmayalım. İŞİN ZOR KISMI BİTTİ! İşte main()’inmiz:

void main()
{
    while(gir!=999){
        clrscr();
        printf("\n\n\n\t\t\t aspecden V0.9 \n");
        printf("\t\t\t Sözlük Programi\n");
        printf("\t\t\t MENU\n");
        printf("\t\t\t 1-) Sözcük ekle \n");
        printf("\t\t\t 2-) Sözcük Görüntüle \n");
        printf("\t\t\t cikis icin 999 \n");
        scanf("%d",&gir);
        if(gir==999)
        { return; }
        else if(gir==1)
        { ekle(); getch(); }
        else if(gir==2)
        { oku(); getch(); }
        else printf("yanlis tusa bastiniz");
    }
}

Programımız ve dosya işlemleri ile ilgili giriş bilgilerimizi tamamladık. Tek eksiğimiz, dosya işlemleri ile yapılmış bu basit sözlük programını yazıp derlemek. Bu yazımızda, kullandığımız fonksiyonlarla ilgili bildirimler ve genel kullanım şekillerine, göz atalım : Bu bilgiler, sizin programı daha iyi anlamanızı ve fonksiyonları hakkında bilginiz olmasını, sağlayacaktır. İlk olarak, gets() fonksiyonunu ele alalım: Bildirimi : char *gets(char *x); Gets, karakter katarlarını okumak için kullanılır ve x sembolü ile gösterilen yere, bilgileri aktarır. Burada dikkat edilmesi gereken husus, sonuna NULL eklenmesidir! Bizim programımızda kullandığımız şekli ile bir dizi içerisine okunan bilgileri yerleştirir.stdio.h başlık dosyasında tanımlanmıştır. fwrite fonksiyonumuzun bildirimi ise şu şekildedir: Bildirimi : size_t fwrite(const void *buf,size_t X,size_t z, FILE *fp); Dosyaya toplu yazım yapmak için kullanılır. fp ile belirtilen dosyaya yazım yapılır. X sekizli uzunlukta buf ile işaret edilen bir dizinin, ilk "z" tane elemanını, fp ile belirtilmiş olan dosyaya yazmaktadır. stdio.h başlık dosyasında tanımlanmıştır. fread fonksiyonu, ise fwrite fonksiyonu ile aynı kalıptadır bilmemiz gerekn tek şey ise bu sefer yazım değil toplu okuma yapılmaktadır. fflush(); Bildirimi: int fflush(FILE *fp); Umarım sizin için yararlı bir yazı olmuştur. Yaratıcılığınızı kullanmayı unutmayın! Geliştirdiğiniz dosya işlemleri ile yapmış olduklarınızı görmek isterim inhoftec@gmail.com adresine yazı ile ilgili eleştirilerinizi ve geliştirmiş olduğunuz programları gönderebilirsiniz. Görüşmek üzere!



C, Algoritmalar ve Çözümlemeler

clock Mart 20, 2005 08:05 by author Volkan Atasever

Hayatımızın her alanında problemlerle karşılaşıyoruz ve bildiğimiz gibi bu konuları çözümleme üstüne odaklanıyoruz. Bilgisayar alanındaki programlama dilleri de aslında bizim ürettiğimiz belli bir mantık içinde görevini yerine getirmek üzere tasarlanmıştır. Yemek, içmek gibi her an farkında olmadan çözmeye çalıştığımız problemlerin karşılıklarını C algoritmaları içerisinde de buluyoruz. Ürettiğimiz çözüm algoritmalarının kaynak kodunu derlemek ve çalışır bir program haline getirmek de, bir mantık çerçevesi içinde, başlama ve bitiş sürecinin arasında kalmış bir evreden ibaret. İlk önce isterseniz basit birkaç algoritma ve çözüm yolları düşünelim ve bunların hangi evrelerden en iyi çözüme gittiğini görelim. Örneğin, çalışan bir saat yapmak istesek, gün, saat, dakika ve saniye cinsinden 4 adet döngüyle kontrol sağlamamız gerekecekti. Fakat bu algoritmayı sadece 1 adet döngü kullanarak da nasıl çözüme ulaşabileceğimizi göstermeye çalışacağım. Çalışan saat örneğimiz ;

int main()
{
     int i,j= 0,x= 0,z=0;
     for(i = 0;i < 61; i++) {
         delay(500);
         if(i > 59) { 
             j++;
             i=0;
             delay(1000);
         }
         if(j > 59){
              x++; 
              j=0;
             delay(1000);
         }
         if(x > 23){
             z++;
             z=0;
             delay(1000);
        }
         printf("%d gun %d saat %d dakika %d saniye\r",z,x,j,i); 
    }
    return 0;
}

Örneğimizde de gördüğünüz gibi; iç içe döngülerle uğraşmak yerine bir adet for döngüsüyle çözüme ulaştık. Bu uygulamamızı, programınızın ne kadar zamandır çalıştığını öğrenmek içinde kullanabilirsiniz. Küçük örneklerle size algoritmaları anlatmaya çalışacağım. Gördüğünüz gibi algoritma kurulamazsa programdan bahsedemeyiz. Kod yazmak bir sanattır ama şu da unutulmaması gerekir ki algoritma yani çözücülük sanatı kod yazma sanatının lokomotifidir. Karmaşık yollardan çözüme ulaşan bir algoritmanın kodu da karmaşık olacaktır. Unutmayalım ki, programlama sanatı hem algoritmayı hem de kodu kapsayan bir birlikteliktir. Bunun da en önemli amacı programın anlaşılabilir ve taşınabilir olmasıdır. Girişimizi yaptıktan sonra ana konumuza yavaş yavaş değinelim bir problem düşünelim ve bunun yapım aşamalarını ortaya çıkartalım. Problemimiz şu olsun: Bir satranç tahtasında aletlerimizi (şah , at, fil vb.), nereye koyarsak koyalım yapabileceği bütün hamleleri bize gösterebilen bir o kadarda yararlı bir uygulama geliştirelim. Bu uygulamamızın adı (programımızın bir adı olmalı değil mi?), hamle v0.9 olsun.İlk öncelikle include’larımızı ve global değişkenlerimizi koyalım;#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define alet tahtagor(tahta)int satir, sutun;
int tahta[8][8]={0};Burada tahta dizimiz, satranç tahtamızı temsil etmektedir(8x8’liktir lakin dizilerin 0’ıncı indis’den başladığını unutmayalım). Satir ve sutun ise tahtamızın bizim oluşturduğumuz, sanal olarak x ve y koordinatlarına böldüğümüz matrisimizin istediğimiz alanına ulaşmakta kullandığımız değişkenlerimizdir.Burada bütün taşların mantığı belli bir kalıba oturduğu için her taşımızı çözümlemek yerine önce at aletimizi çözümlemeyi uygun bulduk yazımızın altında uygulamanın çalıştırılabilir programını bulabilirsiniz.At fonksiyonumu;

void at(int n, int m)
{
    int ilk,son,z;
    int kadir[8][2]={{-2,-1},{-2,1},{2,-1},{2,1},{1,-2},{1,2},{-1,-2},{-1,2}};
   
    for(z=0;z<8;z++){
        ilk = kadir[z][0];
        son = kadir[z][1];
        kolaylik(n,m,ilk,son);
    }
}   

Gördüğünüz gibi bir at fonksiyonu yazdık burada dikkat edilmesi gereken yer kadir dizimiz olacaktır. Kadir dizimizde bir atın gidebileceği yerleri tanımlamış bulunuyoruz. Bir at aleti L şeklinde gideceği için 2 ve 1 rakamları bizim sihirli sayılarımızdır. Bunların oluşturduğu kombinasyonlar, kadir dizinini çok değerli bir hale getirmektedir. Sizde bir satranç tahtasında bu rakamları kullanarak gidebileceği bütün yerleri görebilirsiniz(algoritmanın burada işi nasıl bitirdiğini görebilirsiniz acaba beynimizde 0 ve 1’lerdenmi oluşuyor ;) ). Unutulmaması gereken bir diğer husus ise döngümüzün 8 defa döndüğüdür. At’ımızın gidebileceği maksimum yer sayısı 8’dir. Aynı anda ilk ve son değişkenlerimize x ve y koordinatlarını bildiriyoruz. At fonksiyonumuzun aldığı iki tamsayı parametre ise geldiği gibi fonksiyonun içinden kolaylik fonksiyonumuza geçiyor. İsterseniz kolaylik fonksiyonumuzu inceleyelim(nede olsa C bir fonksiyonlar dili bunu sonuna kadar kullanabiliriz).

void kolaylik(int re, int ke, int z, int l)
{
    int x,y;
   
    system("cls");
    re += z;
    ke += l;
    for(x= 0,y = 0;x < 8, y < 8; x++,y++) {
        if(re > x || ke > y || re < 0 || ke < 0) { }
        else{
            gecerli(re, ke);
        }
    }
}

Kolaylik fonksiyonumuz, 4 parametre almaktadır. Unutmayalım At fonksiyonundan aldığı n,m ve ilk,son değişkenleri ile işlem yapmaktadır. Dizimizin taşmaması ve saçmalamasını önlemek için for döngümüzü işe getirdik eğer taşıyorsa veya saçmalıyorsa da hiçbir şey yapmadan o pozisyonu if deyimimizde pas geçiyor. Eğer bir sorun yoksa gecerli() fonksiyonumuz işletiliyor. Unutulmaması gereken at fonksiyonumuzun aldığı iki parametre değişkeni, at fonksiyonumuz içindeki ve kadir değişkeninden aldığımız değerle toplanıyor.İsterseniz şimdide gecerli() fonksiyonumuzu inceleyelim (fonksiyonların bizi nereye götüreceği belli olmuyor ).İşte geçerli fonksiyonumuz;

void gecerli(int x, int y)
{
    tahta[x][y]=2;
}

Sadece tahtamızdaki hamleleri işliyor. Basit ama o kadarda işin özünü anlatan bir fonksiyon diyebiliriz.Ve uygulamamız da satranç tahtamızı görmek için yazdığım bir fonksiyon;

void tahtagor(int dizi[][8])
{
    int k,l;
    printf("\n\n");
    for(k=0;k<8;k++){
         for(l=0;l<8;l++){
             printf("\t%d",dizi[k][l]);
        }
        printf("\n\n\n\n");
    }
}

Bu kısmı anlatmama gerek yok sanıyorum basitçe dizinin her elemanını bir matris şeklinde yazdırıyor.Evet, işin zor kısmını atlattık bundan sonraki bölümler ince ve dikkat edilmesi gereken noktalar.

void warning()
{
    printf("Ornek 4 5 !!!");
    scanf("%d%d",&satir,&sutun);
    system("CLS");
    satir--;
    sutun--;
    tahta[satir][sutun]=1;
}

Burada satir ve sutun değişkenlerimizi okuyoruz ve 1 azaltıyoruz malum dizilerin güzelliklerinden dolayı.Algoritmalar daha çok bizim atı nasıl işletebileceğimiz kısmında ortaya çıkıyor. Benim yazdığım aletin bütün hamleleri, adlı uygulamanın bu konuda en iyi algoritma olduğunu savunmuyorum, çünkü daha çok çözüm yolları olabildiği gibi daha iyisi ve daha kötüsüde olabilir. main ve hikaye isimli son fonksiyonumuzu da verelim.

int main()
{
    int a;

    hikaye();
    scanf("%d",&a);
    if(a == 1) {
        printf("At’in Konumunu Giriniz");
        warning();
        at(satir, sutun);
    }
    else if(a == 2){
        printf("Fil’in Konumunu Giriniz");
        warning();
        fil(satir, sutun);
    }
    else if(a == 3){
        printf("Kale’nin Konumunu Giriniz");
        warning();
        kale(satir, sutun);
    }
    else if(a == 4){
        printf("Piyon’un Konumunu Giriniz");
        warning();
        piyon(satir, sutun);
    }
    else if(a == 5){
        printf("Sah’in Konumunu Giriniz");
        warning();
        sah(satir, sutun);
    }
    else if(a == 6){
        printf("Vezir’in Konumunu Giriniz");
        warning();
        vezir(satir, sutun);
    }
    alet;
    getch();
    return 0;
}

 

void hikaye()
{
     system("CLS");
     printf("\n\n\tLutfen Bir Alet Seciniz\n");
     printf("\t1-) At\n\t2-) Fil\n\t3-) Kale \n\t4-) Piyon\n\t5-) Sah\n\t6-) Vezir\n");
     printf("\twritten by Volkan Atasever\t inhoftec@gmail.com\n");
}

Evet, uygulamamız bitti sizde kalan diğer taşları programlarsanız bu uygulamanın bir anlamı olacağı kanısındayım.



Volkan Atasever Kimdir ?

E-Mail: volkan(et)volkanatasever.com

Genel Müdür/S4G 
Bilgisayar Mühendisi

mühendislik, edebiyat, retro computer, yazılım, şiir, nümismatik, bilişim, podcast ve bir çok kategoride yazılarım...

Yazılım Kitaplarım

   

Şiir Kitaplarım

Dergi Yazılarım

 

Sosyal Ağlarım

 

 

 

 

Retro Bilgisayar Dergimiz

Türkçe Retro Bilgisayar dergimiz haziran 2016'da ilk sayısını çıkardı. ilk sayı yeni bir internet sitesinden tekrar yayınlanacaktır. Retro camiası tüm dünyada oldukça eğlenceli bir camia. Sizde katkıda bulunmak isterseniz bana mail atmanız yeterli. Dergimiz PDF formatında ve tamamen ücretsizdir.

Yasal Bilgi

Sitedeki yazıları,kodlar ve diğer materyaller Volkan Atasever'e aittir. Yazarı ve siteyi kaynak göstererek yazıları paylaşabilirsiniz.  Copyright(c)2004-2017

Sign In

Yazılım