Ön İşlemci Komutları

C birtakım ön işlemci konutları da içerir. Bu komutlar kod derlenirken dikkate alınırlar. Ön  işlemci komutlarının başına # işareti konur ve her ön işlemci konutundan sonra bir alt satıra geçilmeli, ön işlemci komutunun satırının sonuna ";" konmamalıdır. Şimdi sırayla bu komutlara göz atalım.


#define

Bu ön işlemci komutu programın en başından itibaren bir değeri adlandırır. Programda verilen bu adla her karşılaşıldığında karşılığı olan değerle işlem yapılır. Bu komut kullanılarak yapılan adlandırmalarda geleneksel olarak ismin tamamı büyük harflerden oluşur. Fakat bu zorunlu değildir. Burada verilen isme makro denir.

#define komutu ile bir tanımlama şu formatla yapılır:

#define makro_ismi değer

#include <stdio.h>
#define MAX 40

int main(void){
    int a;
    printf("Enter the temperature: ");
    scanf("%d",&a);
    if(a<MAX)
        printf("OK");
    else
        printf("Not OK");
    return 0;
}

Yukarıdaki örnekte MAX ismi 40 değeri için kullanılmıştır. Örneğin yukarıdaki kod parçası bir prosesteki sıcaklık kontrolü için kullanılabilir. Prosesteki bir yerin sıcaklığının 40 dereceyi geçmesi istenmemektedir. Eğer ileride yeni cihazlarla bu prosesi yürüten şirket sıcaklık sınırını 50 dereceye çıkaracak olursa sadece MAX ismine verdiği değeri değiştirmesi yetecektir. Yukarıdaki örnekte bu değer tek bir yerde kullanıldığı için makro kullanmak çok yararlı görülmeyebilir. Fakat aynı değerin birçok yerde kullanıldığı daha karmaşık kodlarda yararlı olacaktır.

Ayrıca bir makro tanımlanırken bu tanım esnasında bir başka makrodan da yararlanılabilir:

#include <stdio.h>
#define MIN 20
#define MAX MIN+20

int main(void){
    int a;
    printf("Enter the temperature: ");
    scanf("%d",&a);
    if(a<MAX)
        printf("OK");
    else
        printf("Not OK");
    return 0;
}

Örneğin yukarıdaki kodda önce bir MIN makrosu tanımlanmış ve MAX makrosu tanımlanırken ondan yararlanılmıştır. Bu özellik misalen mikro denetleyici programlarken kolaylık sağlamaktadır. Mikro denetleyici programlamak için gerekli olan kütüphaneler mikro denetleyicinin giriş ve çıkışları için makrolar tanımlar. Eğer programlamayı yapan kişi kendi kullanacağı giriş çıkışlar için kütüphanelerin tanımladığı makroları kullanarak yeni bir makro tanımlarsa kullanmak istediği giriş çıkışları değiştirmek istediğinde tüm kodu değil sadece kendi tanımladığı makroları değiştirmesi yeterli olacaktır.

Makrolar fonksiyon gibi de kullanılabilir, bunun için format şudur:

#define makro_ismi(argüman) fonksiyon

#include <stdio.h>
#define MAX(a) a*5+20

int main(void){
    int x=MAX(15);
    printf("%d",x);
    return 0;
}

Mesela yukarıdaki örnekte, girilen bir argümanı 5 ile çarpıp buna 20 ekleyen bir fonksiyon makro olarak tanımlanmıştır. Argüman olarak 15 girildiğinden sonuç 95 olacaktır. Makroların bu şekilde kullanılması fonksiyon çağırma işlemi olmadığı için programın çalışmasını hızlandırabilir, fakat kodun boyutunu büyütecektir.

Fonksiyonların bu şekilde tanımlanmasında kullanılan iki adet de işlem bulunmaktadır. Bunlar # ve ##'dir. # işlemi argümanı iki tırnak içine alınmış dizgiye çevirmeye yarar ve şu şekilde kullanılır:

#define fonksiyon_ismi(argüman_ismi) #argüman_ismi

#include <stdio.h>
#define func(s) #s

int main(void){
    printf(func(Test));
    return 0;
}

Yukarıdaki örnekte ekrana "Test" yazdırılacaktır.

## işlemi de verilen iki argümanı birleştirmeye yarar ve şu şekilde kullanılır:

#define fonksiyon_ismi(argüman_ismi1, argüman ismi2) argüman_ismi1 ## argüman_ismi2

#include <stdio.h>
#define func(a,b) a##b

int main(void){
    int test=99;
    printf("%d",func(te,st));
    return 0;
}

Yukarıdaki örnekte "te" ve "st" ifadeleri birleştirilecek ve bu şekilde 99 değeri yazdırılacaktır.


#include

Bu ön işlemci komutunu daha önceki örneklerimizde de kullanmıştık. Bu komutun kullanım şekli şöyledir:

#include <dosya_ismi> veya #include "dosya_ismi"

Dosya ismi yerine bir kütüphane ismi veya daha önceden yazılmış bir C programının ismi yazılabilir. Bu durumda yazılan program bu dosyalardaki fonksiyonlar ve makroları da kullanabilir.

Bu komut zaten daha önceki tüm örneklerde yer aldığı ve bundan sonrakilerde de yer alacağı için bu konuda burada örnek verilmeyecektir.

Dosya isminin etrafında küçüktür ve büyüktür işaretlerinin olmasıyla tırnak işaretlerinin olması arasında küçük bir fark vardır. Bu nüans ismi verilen dosyanın hangi klasörde aranacağını etkiler. Fakat küçüktür ve büyüktür işaretleri arasındaki yazım çok özel bir durum olmadıkça yeterlidir ve genellikle bu format tercih edilir.


#if, #else, #elif, #endif

#if ön işlemci komutu if komutu ile hemen hemen aynı mantıkla çalışır. Verilen şart sağlanıyorsa komutun içerdiği işlem gerçekleşir. Ön işlemci komutlarında parantez kullanılmadığı için #if ön işlemcisinin sınırlarını belirtmek için #endif ön işlemci komutu kullanılır. Bu komutların kullanım formatı şu şekildedir:

#if koşul
Çalıştırılacak kod
#endif

#include <stdio.h>
#define YEAR 2010

int main(void){
    #if YEAR<2013
    printf("It is a past date");
    #endif
    return 0;
}

Yukarıdaki örnekte karşımıza "It is a past date" ifadesi çıkacaktır. Çünkü YEAR makrosu 2010 olarak tanımlanmıştır. Fakat YEAR makrosuna değer olarak 2014 verilecek olursa program hiçbir çıktı vermeyecektir.

#else ön işlemci komutu ise yine else komutuyla benzer çalışır. #else ön işlemci komutu kullanılarak #if ön işlemci komutunda verilen şartın sağlanmadığı durumda yapılacak işlemler belirtilebilir.

#include <stdio.h>
#define YEAR 2014

int main(void){
    #if YEAR<2013
    printf("It is a past date");
    #else
    printf("You can update the values");
    #endif
    return 0;
}

Yukarıdaki örnekte YEAR makrosu 2014 olarak tanımlandığı için #if ön işlemci komutunda verilen şart sağlanmayacak bu nedenle #else ön işlemci komutunun belirttiği işlemler yapılacaktır. Burada görüldüğü gibi #else ön işlemci komutu hem #if ön işlemci komutunun içerdiği işlemlerin sonunu hem de kendi işlemlerinin başını belirtmektedir. Araya ayrıca bir #endif koyulmamalıdır.

#elif ön işlemcisi ise else-if yapısının görevini görür. Bu ön işlemci komutu da yine #else ön işlemcisi gibi hem kendinden önceki ön işlemci komutunun bitişini hem de kendi işlemlerinin başlangıcını belirtir. #if ve #elif ön işlemcilerden birisinin şartı sağlanırsa diğerleri test edilmez.

#include <stdio.h>
#define YEAR 2014

int main(void){
    #if YEAR<2009
    printf("It is a past date");
    #elif YEAR<2013
    printf("You can only view the information");
    #else
    printf("You can update the values");
    #endif
    return 0;
}

Yukarıdaki örnekte hem #if hem #elif hem de #else kullanılmıştır. YEAR makrosuna yazılacak 2009'dan küçük değerler için #if ön işlemci komutunun, 2009 ve 2012 arasındaki değerleri için #elif ön işlemci komutunun, diğer değerler için ise #else ön işlemci komutunun altındaki işlem yapılacaktır. YEAR makrosunun karşısındaki sayıyı değiştirerek bunun böyle olduğunu görebilirsiniz.

Bu ön işlemci komutlarının koşul kısmında değişken kullanılamaz. Kullanılan değerler sabit ya da ön işlemci tanımlı makrolar olmalıdır. Çünkü bu komutlar program çalışırken değil kod derlenirken değerlendirilirler. Zaten bu ön işlemci komutlarını if ve else komutlarından ayıran da budur.


#ifdef

Bir makro isminin daha önceden tanımlanmış olup olmadığını kontrol eder. Özellikle derleyicinin kendisinin tanımladığı makrolar olup olmadığını test etmek için kullanılır. Eğer makro tanımlanmışsa altındaki kod çalıştırılır.Bu ön işlemci komutunun sınırını belirtmek için de #endif komutu kullanılır. Format kabaca şu şekildedir:

#ifdef makro_ismi
çalıştırılacak kod;
#endif


#ifndef

#ifdef ile aynı şeyi kontrol eder. Fakat bu komut kullanıldığında eğer belirtilen makro ismi tanımlı değilse altındaki kod çalıştırılır. Bu komutun kullanılma kalıbı da kabaca şöyledir:

#ifndef makro_ismi
çalıştırılacak kod;
#endif

Bu ön işlemci komutlarıyla birlikte de #else ve #elif ön işlemci komutları kullanılabilir.

#include <stdio.h>
#define MAX 5

int main(void){
    #ifdef MAX
    printf("MAX is defined\n");
    #else
    printf("MAX is not defined\n");
    #endif
    #ifndef MIN
    printf("MIN is not defined\n");
    #else
    printf("MIN is defined\n");
    #endif
    return 0;
}

Yukarıda MAX isimli bir makro tanımlanmış fakat MIN isimli bir makro tanımlanmamıştır. Bu duruma göre #ifdef ve #ifndef için örnek yapılmıştır. İki komutta da şart sağlanacaktır.

"#ifdef makro_ismi" yerine "#if defined makro_ismi" ve "#ifndef makro_ismi" yerine "#if !defined makro_ismi" kalıpları da kullanılabilir.

#include <stdio.h>
#define MAX 5

int main(void){
    #if defined MAX
    printf("MAX is defined\n");
    #else
    printf("MAX is not defined\n");
    #endif
    #if !defined MIN
    printf("MIN is not defined\n");
    #else
    printf("MIN is defined\n");
    #endif
    return 0;
}

Bu sefer yukarıdaki örnek defined kullanılarak verilmiştir.


#error

Bu ön işlemci komutu çalıştırılacak olursa derleme durur ve belirtilen bir hata mesajı görüntülenir. Bu ön işlemci komutunun kullanım kalıbı kabaca şöyledir.

#error Görüntülenecek_mesaj

#include <stdio.h>
#define MAX 5

int main(void){
    #if defined MAX
    #error MAX is defined
    #endif
    return 0;
}

Bu örnekte kod derlenmeyecektir. Çünkü MAX tanımlı olduğu için #ifdef komutu ile #error komutuna erişilmiştir. Derleyici "MAX is defined" şeklinde bir hata verecektir.


#undef

Önceden yapılan bir makro tanımlama işlemini iptal eder. Özellikle derleyici veya kullanılmak istenen kütüphane istenmeyen bir makro tanımlıyorsa bunu iptal etmeye yarar.

#include <stdio.h>
#define MAX 5
#undef MAX

int main(void){
    #if defined MAX
    #error MAX is defined
    printf("MAX is defined\n");
    #else
    printf("MAX is not defined\n");
    #endif
    #if !defined MIN
    printf("MIN is not defined\n");
    #else
    printf("MIN is defined\n");
    #endif
    return 0;
}

Bu örnekte MAX makrosu #undef komutu ile iptal edildiği için #error komutuna erişilmeyecektir. Çünkü MAX artık tanımlanmış değildir. Çıktımız da şu olacaktır:

MAX is not defined
MIN is not defined

Ayrıca bilinmesi yararlı olacak bir başka şey de #undef ön işlemci komutuna erişilene kadar makroların tanımlı kalacağıdır. Bu sayede istenen makroların sadece belli bir kısımda kullanılması sağlanır.


#line

C'de koddaki satır bilgisi __LINE__ makrosunda tutulur. Programın ismi de __FILE__ makrosunda depolanır. #line ön işlemci komutu bu bilgileri değiştirmeye yarar ve kullanım şekli şudur:

#line yeni_satır_değeri "yeni_dosya_ismi"

#include <stdio.h>

int main(void){
    printf("%s\t",__FILE__);
    printf("%d\n",__LINE__);
    #line 1 "example";
    printf("%s\t",__FILE__);
    printf("%d\n",__LINE__);
    return 0;
}

Yukarıdaki örnekte __FILE__ ve __LINE__ makrolarının önce normal içerikleri yazdırılmıştır. Bu durmda dosyanın ismi ve __LINE__ makrosunun bulunduğu satır 5. satır olduğu için 5 sayısı ekranda görünecektir.
Daha sonra #line ön işlemci komutu ile bu bilgiler değiştirmiştir. __FILE__ makrosu "example" kelimesini depolamaktadır. __LINE__'ın değeri de 1 olmuştur. Bir alt satırda __LINE__'ın değeri birdir. Bir sonraki satırda bunun değeri 1 artacağı için karşımıza "example" kelimesi ve 2 sayısı çıkacaktır.


#pragma

Bu ön işlemci komutu derleyiciye verilecek özel talimatlar için ayrılmıştır ve derleyiciye göre kullanım alanları değişmektedir. C99 standartları ile birlikte bu ön işlemci komutu yerine kullanılabilecek _Pragma işlemi de eklenmiştir. Bu ön işlemci komutu ile kullanılabilecek talimatları öğrenmek için derleyicinin dokümanları incelenmelidir.


Ön Tanımlı Makrolar

C'de bazı makrolar ön tanımlıdır. Bunlardan __LINE__ ve __FILE__ #line ön işlemci komutunda anlatılmıştır.

İki makro derlenmenin gerçekleştiği tarih ve zamanı tutar. __DATE__ makrosu "ay/gün/yıl" şeklinde derlenme tarihini tutarken __TIME__ makrosu da "saat: dakika: saniye" şeklinde derlenme zamanını tutar.

__STDC__ makrosu ise derleyicinin C standartlarını sağlayıp sağlamadığını depolar. 1 derleyicinin standartları uyduğunu gösterirken 0 uymadığını belirtir. Aşağıdaki örnekte bu makroları kullanarak istenenler yazdırılmıştır:

#include <stdio.h>

int main(void){
    printf("%s\n",__DATE__);
    printf("%s\n",__TIME__);
    printf("%d\n",__STDC__);
    return 0;
}

C99 bunlara iki yeni makro daha eklemiştir. Bunlar __STDC_HOSTED__ ve __STDC_VERSION__'dur.  __STDC_HOSTED__ bir işletim sistemi varsa 1 yoksa 0'dır. __STDC_VERSION__ ise kullanılan C versiyonunu gösterir.

#include <stdio.h>

int main(void){
    printf("%d\n",__STDC_HOSTED__);
    printf("%d\n",__STDC_VERSION__);
    return 0;
}




Listeler <<<<< Temel C >>>>> Yorumlar