Temel C Programlama -53- Ön İşlemci Komutları (Preprocessor)

Temel C programlama konumuzun en sonunda size ön işlemci komutlarından bahsedeceğiz. Ön işlemci komutları ile normal C komutlarını biraz ayırmamızda fayda vardır. Ön işlemci komutları derleyiciye göre değişiklik gösterebilir ve bu özel komutları derleyicilerin kılavuzlarında okuyabilirsiniz. Fakat C dilinin olmazsa olmazı bazı ön işlemci komutları da vardır. Biz konumuzda standart dışı komutlara yer vermeyeceğiz. Kullandığınız platform ve derleyiciye göre değişen bu komutları sizin öğrenip kullanmanız gereklidir.

Ön işlemci komutlarını şimdiye kadar bütün programlarımızda kullandık. Örneğin #include ile dosyaya bir kütüphane başlık dosyası eklerken kullandığımız #include komutu bir ön işlemci komutuydu. Bu komutlar doğrudan derleyiciye hitap etmekte ve program komutu değil derleyici komutu olarak çalışmaktadır. O yüzden derleyiciler program kodu derlenmeden önce bu komutları çalıştırmaktadır. Ön işlemci komutları sadece bir kütüphane dosyasını eklemek için değil sabit tanımlamak, makro tanımlamak, şartlı tanımlar ve derleyici yönergeleri için kullanılmaktadır. Şimdi belli başlı ön işlemci komutlarına göz atalım.

#include yönergesi

Biz derslerin en başında “Merhaba Dünya!” uygulamasında bile #include yönergelerini kullandık. C dili oldukça sade yapıya sahiptir. Bu sade yapıdan dolayı #include ile kütüphane dosyalarını eklemezsek hemen hemen hiçbir şey yapamayız. En temel işlerden biri olan giriş ve çıkış fonksiyonları bile ayrı bir kütüphanede yer almakta ve #include ile programa dahil edilmektedir. Eğer böyle olmasaydı dilin içine ve standarta eklemek gerekecekti ve bütün platformlara uyumlu olmayacaktı. Örneğin AVR programlarken kimse printf() veya scanf() gibi fonksiyonları kullanma gereği duymaz. O halde bunlar dilin kendi yapısı olamayacağı için ayrı kütüphane dosyaları olarak bize gelmiştir. Bu kütüphane dosyaları harici kütüphaneler ve standart kütüphaneler olarak ikiye ayrılmaktadır. Standart kütüphanelerde belli başlı işleri yürüten belli fonksiyonlar yer almaktadır. Biz #include yönergesini şu iki şekilde kullanabiliriz.

#include <dosyaadi>
#include "dosyadi"

Burada birinde < ve > kullanılırken diğerinde çift tırnak arasına (” “) yazılmıştır. Bu ikisi arasında büyük fark vardır ve karıştırmanız halinde dosya bulunamayabilir. Kural olarak < ve > işaretleri yeri bilinen standart kütüphane dosyalarını eklemek için kullanılırken çift tırnak (“”) ise yeri bilinmeyen kullanıcı dosyalarını eklemek için kullanılır. < ve > arasına yazdığınız dosyanın yolu derleyicide belirlenmiştir ve derleyici bunun nerede olduğunu bilmektedir. Fakat iki tırnak arasına dosyanın yolu belirtilmezse bu dosya bulunamaz.

#define yönergesi

#define yönergesi en başta sabit sembollerini tanımlamak için kullanılmaktadır. Burada sabitleri tanımlamanın oldukça esnek olduğunu söylememiz gerekir. Aslına bakarsanız #define yönergesi ile derleyici bir yerde bir sembolü gördüğü zaman onu keser ve bizim yazdığımızı oraya yapıştırır. Böylelikle kodu baştan sona düzenlemiş olur. Şimdi #define yönergesinin söz dizimini size gösterelim.

#define sembol yerlestirilecek_deger

Buradan pek bir şey anlamadıysanız bunun nasıl kullanılacağına dair bir örnek verelim. #define yönergesini biz sabit değişkenler yerine kullanabiliriz. Üstelik hafızadan da tasarruf edeceğimiz için gömülü sistemlerin vazgeçilmezlerinden biridir. const int pi = 3.14 demek yerine #define PI 3.14 dediğimizde derleyici PI yazan yeri gördüğü zaman 3.14 ifadesini oraya yapıştıracaktır. Böylelikle değişken oluşturup bunu da hafızaya kaydedip sürekli sürekli hafızadan okumak zorunda kalınmaz. Değişmeyen bir değer olduğu için sabit olarak kullanmak daha performanslıdır.

#define PI 3.14 
const float PI = 3.14F;

Biz float diye değişken tanımlayarak 32-bitlik yani 4 baytlık bir yer işgal ettiğimiz gibi mikroişlemci her PI üzerinde işlem yaparken bu PI değişkeninin olduğu adresi okuyarak ekstradan komut harcamak zorunda kalacaktır. Oysa ki biz sabit olarak tanımlasak bu 3.14 değeri komutun içerisinde yer alacak ve okunur okunmaz işletilecektir. Fakat biz programın her yerine 3.14 yazmak yerine kolayca PI yazmak istiyorsak #define PI 3.14 yazmamız gerekecektir. Üstelik #define yönergesi sadece sabitler için kullanılmaz. Programın programlama aşamasında bazı ayarlarını değiştirmek için de kullanabiliriz. Örneğin lambaları yakıp söndüren bir programımız var ve bu aşamada bir bekleme süresi tahsis etmemiz gerekiyor. Bu durumda ya değişken tanımlayacağız ve hafızadan harcayacağız ya da #define ile tanım yapacağız.

#define bekleme 100 

// komutlar
// komutlar
delay(bekleme);
// komutlar
// komutlar
delay(bekleme);
// komutlar
// komutlar
delay(bekleme);

Gördüğünüz gibi bekleme sabitini pek çok yerde kullanıyoruz. Biz bu sembolü kullanmak yerine doğrudan sabit değer olan 100 değerini yazsaydık değiştirmek istediğimiz zaman bütün kodu taramamız gerecekti. Burada #define yönergesinde yer alan değeri değiştirmekle bütün program üzerinde değişikliği yapmış oluyoruz. Üstelik bunun için de değişken kullanmamıza gerek kalmıyor.

#define sadece bununla sınırlı kalmaz. Yukarıda anlattığımız sabit değişkenleri kullanmanın daha ekonomik yollarından biriydi. Biz #define ile sadece değer değil bir kod bloku veya bir komutu hatta bir anahtar kelimeyi bile değiştirebiliriz. Aşağıda bunun birkaç örneğini görebiliriz.

#define LEDYAK digitalWrite(16,HIGH)
#define if eger



LEDYAK; 

eger (i < 10)
komutlar;

Bu sayede kendi programlama dilimizi yapmaya kadar giden bir yolun olduğunu görsek de bunlar standart dışı olacağından biz bildiğimiz C programlama dilinden şaşmayalım.

#define yönergesinin en önemli başka bir kullanım alanı ise makrolardır. Makrolar aynı inline (satır arası) fonksiyonlar gibi görev yapmaktadır. Derleyici burada kopyala yapıştır yapmaktan öte gidemediği için pratik olsa da her zaman ekonomik olduğunu söyleyemeyiz. Şimdi örnek bir makroya göz atalım.

#define DIKDORTGEN_ALANI( x, y ) ( ( x ) * ( y ) )

Burada dikdörtgenin alanını hesaplayan bir makro görmekteyiz. Bunu aynı fonksiyonu nasıl kullanıyorsak öyle kullanabiliriz. Örnek bir kod şöyle olabilir.

alan = DIKDORTGEN_ALANI( 5 , 10 );

#define ile tanımlanan sabitler #undef ile de kaldırılabilir. Bu makrolar kütüphane ve derleyici dosyalarında sıkça kullanılmaktadır. Bazen bu makroların iç yapısını ve ne işe yaradığını bilmeniz gerektiğinden dolayı karşınıza çıktığı zaman iyi çözümlemeniz gerekecektir.

Şartlı Derleme

Biz her zaman aynı programı aynı amaç doğrultusunda yazmayız. Örneğin yazdığımız bir program üç farklı amaç için kullanılıp sadece birkaç satır kod fark içeriyorsa bazı komutların bazı durumda derlenmesini bazen de bu komutların programdan çıkarılmasını isteyebiliriz. Bu durumda şartlı derleme işlemi adı verilen bir işlem kullanılır. Biz burada tanımladığımız değerlere göre bazı program kodlarını derlemeye alabilir veya bunları çıkarabiliriz. Bir de eğer bazı sabitleri tanımlamadıysak kütüphanede bu sabitleri standart olarak tanımlanmasını isteyebiliriz. İndirdiğiniz kütüphaneleri incelediğinizde bu tarz kullanımların fazlaca olduğunu göreceksiniz.

#if yönergesi

Bu yönerge if komutu gibi çalışmaktadır. Fakat derleyici bazında yapılan bu işlemle if bloku içerisinde yer alan program kodu programa dahil edilmektedir. Eğer if şartı sağlanmazsa bu kod bloku programdan tamamen çıkartılmakta ve derleme esnasında programa dahil edilmemektedir. #if yönergesi her zaman #endif yönergesi ile bitmek zorundadır.

#if DEBUG
    printf("Hata Ayiklama Surumu");
#endif

Kütüphanede böyle bir kodu gördüğümüzde burada DEBUG adında yer alan sabitin değerini bilmemiz gerektiğini anlamamız gereklidir. Burada DEBUG değerinin 1 veya 0 olmasına göre bu komut programa dahil edilecektir. Bu durumda başlık dosyasında kütüphane ayarlarının olduğu kısma bakmamız ve bunu değiştirmemiz gereklidir. Pek çok C kütüphanesinde .h başlık dosyasındaki ayarları bizim değiştirmemiz gerekebilir. Burada da #define DEBUG 0 ya da #define DEBUG 1 yaparak bu kodun programa dahil olup olmamasını belirleyebiliriz.

#ifdef yönergesi

Bu yönerge “Eğer tanımlanmışsa” anlamı taşımaktadır. Yani biz bir sembolü #define ile tanımladıysak şart bloku içindeki program komutları programa dahil edilecektir ve işletilecektir.Biz bir değer tanımladıysak kullanıcıya yönelik bir program işletilebilir. Örnek programda bunu görmekteyiz.

#include <stdio.h>

#define YAS 10

int main()
{
   #ifdef YAS
   printf("Kullanici %i Yasindadir\n", YAS);
   #endif

   return 0;
}

Burada eğer kullanıcı YAS diye bir tanım yaparsa program kullanıcının yaşını da ekrana yazdırmaktadır. Eğer böyle bir tanım yoksa bu komutun işletilmesine ve hatta programa dahil edilmesine hiç gerek yoktur.

#ifndef yönergesi

Genelde #ifndef yönergesi tanımlanmamış bir değere standart bir değer atamak için kullanılır. Bir kütüphanede tanımlanmamış bir sabit olursa ve bu sabit pek çok yerde kullanılıyorsa kütüphane sırf değer tanımlanmadığı yüzünden çalışmayacaktır. Bunu kullanıcı unutsa da kütüphaneyi yazan programcı bunu hesaba katarak standart bir değer belirler.

#ifndef KULLANICI_ADI
#define KULLANICI_ADI Player
#endif

Burada oyunlardan aşina olduğunuz bir durum karşımıza çıkmakta. Eğer kullanıcı adını kullanıcı belirlemezse oyun kullanıcıya “Player” diyecek ve sunucularda adını böyle yazacaktır.

#ifndef yönergesinin en önemli kullanım alanlarından biri de kütüphane başlık dosyalarının tekrarlanmasını önlemektir. İncelediğiniz kütüphanelerde her zaman başlık dosyalarının şu şekilde başladığını görürsünüz.

#ifndef MAIN_H_
#define MAIN_H_

// Burada bütün başlık dosyası kodu var

#endif

Siz de başlık dosyası yazarken bu alışkanlığı edinirseniz iyi olur.

Bu #if, #ifdef ve #ifndef yönergeleriyle beraber #elif (else if) ve #else yönergeleri de kullanılmaktadır. Bu yönergeler aynı normal if şart komutlarında olduğu gibi daha karmaşık şart işlemlerini yapmamızı sağlamaktadır.

#warning ve #error yönergeleri

Biz program yazarken derleyiciye hata veya uyarı mesajı vermek istiyorsak bu yönergeleri kullanırız. Bazı durumlarda bizim kodumuzu kullanan programlayıcının ne yapacağını kestiremeyiz. Bu durumda böyle yaptığı zaman hata veya uyarı mesajı vermek zorunda kalabiliriz. Elbette bu kütüphane yazanları ilgilendiren bir durumdur. Nadiren de olsa bazen kendi yazdığımız kütüphanelerde de bu hatalardan korunmak için kendimize mesaj verme ihtiyacı duyabiliriz. #warning ile yazdığımız program derlense de yazdığımız hata mesajı derleyici konsolunda programcıya gösterilecektir. #error ise program derlemesini durdurur. Örneğin benim program içerisine yazdığım #error HATA HATA HATA mesajı derleyici konsolunda şöyle göründü.

warning ve #error yönergelerinin söz dizimi şu şekildedir.

#warning Uyari mesaji 
#error Hata Mesaji

Buraya kadar önişlemciler hakkında öğrendiğiniz bilgiler sizin için yeterli olacaktır. Bunları yazmadan önce kütüphaneleri kullandığınız zaman kütüphaneler içerisinde göreceksiniz ve anlayıp yorumlayabilmeniz gerekecek. Siz nasıl kod yazılacağını öğrenmek istiyorsanız kitapları değil yazılmış hazır kodları ve kütüphaneleri incelemeniz gereklidir. Kitaplar ise bu kodları nasıl anlayacağınızı size bildirir.

Last updated