ÖNİŞLEMCİ KAVRAMI Şimdiye kadar tek bir yapı halinde ele aldığımız C derleyicileri aslında, iki ayrı modülden oluşmaktadır: 1. Önişlemci Modülü 2. Derleme Modülü İsim benzerliği olsa da önişlemcinin bilgisayarın işlemcisi ya da başka bir donanımsal elemanıyla hiçbir ilgisi yoktur, önişlemci tamamen C derleyicisine ilişkin yazılımsal bir kavramdır. Önişlemci, kaynak program üzerinde birtakım düzenlemeler ve değişiklikler yapan bir ön programdır. Önişlemci programının bir girdisi bir de çıktısı vardır. Önişlemcinin girdisi kaynak programın kendisidir. Önişlemcinin çıktısı ise derleme modülünün girdisini oluşturur. Yani kaynak program ilk aşamada önişlemci tarafından ele alınır. Önişlemci modülü kaynak programda çeşitli düzenlemeler ve değişiklikler yapar, daha sonra değiştirilmiş ve düzenlenmiş olan bu kaynak program derleme modülü tarafından amaç koda dönüştürülür. kaynak kod -> önişlemci -> derleyici -> amaç program C programlama dilinde # ile başlayan bütün satırlar önişlemciye verilen komutlardır. # karakterinin sağında bulunan sözcükler ki bunlara önişlemci komutları denir (preprocessor directives), önişlemciye ne yapması gerektiğini anlatır. Örneğin: #include #define #if #ifdef #ifndef hepsi birer önişlemci komutudur. Önişlemci komutları derleyici açısından anahtar sözcük değildir # karakteriyle birlikte anlam kazanırlar, yani istersek include isimli bir değişken tanımlayabiliriz ama bunun okunabilirlik açısından iyi bir fikir olmadığını söyleyebiliriz. Önişlemci komutlarını belirten sözcükler ancak sol taraflarındaki # karakteriyle kullanıldıkları zaman özel anlam kazanırlar ki bunlara önişlemci anahtar sözcükleri de diyebiliriz. Önişlemci amaç kod oluşturmaya yönelik hiçbir iş yapmaz. Önişlemci kaynak programı # içeren satırlardan arındırır. Derleme modülüne girecek programda artık # içeren satırlar yer almayacaktır. Bir çok önişlemci komutu olmakla birlikte şimdilik yalnızca #include komutu ve #define önişlemci komutlarını inceleyeceğiz. Geriye kalan önişlemci komutlarını da ileride detaylı olarak inceleyeceğiz. #include önişlemci komutu genel kullanım biçimi: #include <dosya ismi> ya da #include "dosya ismi" #include, ilgili kaynak dosyanın derleme işlemine dahil edileceğini anlatan bir önişlemci komutudur. Bu komut ile önişlemci belirtilen dosyayı diskten okuyarak komutun yazılı olduğu yere yerleştirir. (metin editörlerindeki copy – paste işlemi gibi) #include komutundaki dosya ismi iki biçimde belirtilebilir: 1. Açısal parantezlerle: #include <stdio.h> #include <time.h> 2. Çift tırnak içerisinde: #include "stdio.h" #include "deneme.c" Dosya ismi eğer açısal parantezler içinde verilmişse, sözkonusu dosya önişlemci tarafından yalnızca önceden belirlenmiş bir dizin içerisinde aranır. Çalıştığımız derleyiciye ve sistemin kurulumuna bağlı olarak, önceden belirlenmiş bu dizin farklı olabilir. Örneğin: \TC\INCLUDE \BORLAND\INCLUDE \C600\INCLUDE gibi. Benzer biçimde UNIX sistemleri için bu dizin, örneğin: /USR/INCLUDE biçiminde olabilir. Genellikle standart başlık dosyaları önişlemci tarafından belirlenen dizinde olduğundan, açısal parantezler ile kaynak koda dahil edilirler. Dosya ismi iki tırnak içine yazıldığında önişlemci ilgili dosyayı önce çalışılan dizinde (current directory), burada bulamazsa bu kez de sistem ile belirlenen dizinde arayacaktır. Örneğin: C:\SAMPLE dizininde çalışıyor olalım. #include "strfunc.h" komutu ile önilemci strfunc.h dosyasını önce C:\SAMPLE dizininde arar. Eğer burada bulamazsa bu kez sistem ile belirlenen dizinde arar. Programcıların kendilerinin oluşturdukları başlık dosyaları genellikle sisteme ait dizinde olmadıkları için iki tırnak içinde kaynak koda dahil edilirler. #include ile koda dahil edilmek istenen dosya ismi dosya yolu da (path) de içerebilir: #include <sys\stat.h> #include "c:\headers\myheader.h" .... gibi. Bu durumda açısal parantez ya da iki tırnak gösterimleri arasında bir fark yoktur. Her iki gösterimde de önişlemci ilgili dosyayı yalnızca yolun belirttiği dizinde arar. #include komutu ile yalnızca baslık dosyalarının koda dahil edilmesi gibi bir zorunluluk yoktur. Herhangi bir kaynak dosya da bu yolla kaynak koda dahil edilebilir. #include "topla.c" int main() { toplam = a + b; printf("%d", toplam); return 0; } topla.c int a = 10; int b = 20; int toplam; Yukarıdaki örnekte #include "topla.c" komutu ile önişlemci komutunun bulunduğu yere topla.c dosyasının içeriğini yerleştirecek böylece prog.c dosyası derlendiğinde herhangi bir hata oluşmayacaktır. Çünkü artık derleme işlemine girecek dosya: int a = 10; int b = 20; int toplam; int main() { toplam = a + b; printf("%d", toplam); return 0; } şeklinde olacaktır. #include komutu kaynak programın herhangi bir yerinde bulunabilir. Fakat standart başlık dosyaları gibi, içerisinde çeşitli bildirimlerin bulunduğu dosyalar için en iyi yer kuşkusuz programın en tepesidir. #include komutu içiçe geçmiş (nested) bir biçimde de bulunabilir. #define Önişlemci Komutu #define önişlemci komutu text editörlerindeki bul ve değiştir özelliği (find & replace) gibi çalışır. Kaynak kod içerisindeki bir yazıyı başka bir yazı ile değiştirmek için kullanılır. Önişlemci define anahtar sözcüğünden sonra boşlukları atarak ilk boşluksuz yazı kümesini elde eder. (Buna STR1 diyelim.) Daha sonra boşlukları atarak satır sonuna kadar olan tüm boşlukların kümesini elde eder. (Buna da STR2 diyelim) Kaynak kod içerisinde STR1 yazısı yerine STR2 yazısını yerleştirir. (STR2 yazısı yalnızca boşluktan ibaret de olabilir. Bu durumda STR1 yazısı yerine boşluk yerleştirilir, yani STR1 yazısı silinmiş olur.) Örnekler : #define SIZE 100 önişlemci komutuyla, önişlemci kaynak kod içerisinde gördüğü tüm SIZE sözcükleri yerine 100 yerleştirecektir. Derleme modülüne girecek kaynak programda artık SIZE sözcüğü hiç yer almayacaktır. #define kullanılarak bir yazının bir sayısal değerle yer değiştirmesinde kullanılan yazıya "sembolik sabit" (symbolic constants) denir. Sembolik sabitler nesne değildir. Derleme modülüne giren kaynak kodda sembolik sabitler yerine bunların yerini sayısal ifadeler almış olur. Sembolik sabitler basit makrolar (simple makro) olarak da isimlendirirler ama biz "sembolik sabit" demeyi yeğliyoruz. Sembolik sabitlerin kullanılmasında dikkatli olunmalıdır. Önişlemci modülünün herhangi bir şekilde aritmetik işlem yapmadığı yalnızca metinsel bir yer değiştirme yaptığı unutulmamalıdır: #define MAX 10 + 20 int main() { int result; result = MAX * 2; printf("%d\n", result); return 0; } Yukarıdaki örnekte result değişkenine 50 değeri atanır. Ancak ön işlemci komutunu #define MAX (10 + 20) şeklinde yazsaydık, result değişkenine 60 değeri atanmış olurdu. Bir sembolik sabit başka bir sembolik sabit tanımlamasında kullanılabilir. Örneğin: #define MAX 100 #define MIN (MAX - 50) Önişlemci " " içerisindeki yazılarda değişiklik yapmaz. (stringlerin bölünemeyen atomlar olduğunu hatırlayalım.) Yer değiştirme işlemi büyük küçük harf duyarlığı ile yapılır. Ancak sembolik sabitler geleneksel olarak büyük harf ile isimlendirilirler. Bunun nedeni, kodu okuyan kişinin değişkenlerle sembolik sabitleri ayırt edebilmesine yardımcı olmaktır. Bilindiği gibi geleneksel olarak değişken isimlendirmesinde C dilinde ağırlıklı olarak küçük harfler kullanılmaktadır. #define komutu ile ancak değişkenler ve anahtar sözcükler yer değiştirilebilir. Sabitler ve operatörler yer değiştiremez. Aşağıdaki #define önişlemci komutları geçerli değildir: #define + - #define 100 200 Sembolik sabitler C dilinin değişken isimlendirme kurallarına uygun olarak isimlendirilir: #define BÜYÜK 10 tanımlamasında hata oluşur. (Hata tanımlama satırında değil sembolik sabitin kod içinde kullanıldığı yerlerde oluşur. Tanımlanan sembolik sabit kod içinde kullanılmazsa hata da oluşmayacaktır.) Önişlemci #include komudu ile kaynak koda dahil edilen dosyanın içerisindeki önişlemci komutlarını da çalıştırır. İçinde semboli sabit tanımlamaları yapılmış bir dosya #include komudu ile kaynak koda dahil edildiğinde, bu sembolik sabitler de kaynak kod içinde tanımlanmış gibi geçerli olur. #define sembolik sabit tanımlamalarında stringler de kullanılabilir : #define HATA_MESAJI "DOSYA AÇILAMIYOR \n" ... printf(HATA_MESAJI); ... Sembolik sabit tanımlamasında kullanılacak string uzunsa okunabilirlik açısından birden fazla satıra yerleştirilebilir. Bu durumda son satır dışında diğer satırların sonuna "\" karakteri yerleştirilmelidir. Önişlemci değiştirme işlemini yaptıktan sonra #define komutlarını koddan çıkartır. Okunabilirlik açısından tüm sembolik sabit tanımlamaları alt alta getirecek şekilde yazılmalıdır. Seçilen sembolik sabit isimleri kodu okuyan kişiye bunların ne amaçla kullanıldığı hakkında fikir vermelidir. C'nin başlık dosyalarında bazı sembolik sabitler tanımlanmıştır. Örneğin stdio.h içerisinde #define NULL 0 biçiminde bir satır vardır. Yani stdio.h dosyası koda dahil edilirse NULL sembolik sabiti 0 yerine kullanılabilir. math.h içinde de pek çok matematiksel sabit tanımlanmıştır. Bir sembolik sabitin tanımlanmış olması kaynak kod içerisinde değiştirilebilecek bir bilginin olmasını zorunlu hale getirmez. Tanımlanmış bir sembolik sabitin kaynak kod içerisinde kullanılmaması hataya yol açmaz. #define Önişlemci Komutu Neden Kullanılır 1. Okunabilirliği ve algınalabilirliği artırır. Bir takım sabitlere onların ne amaçla kullanıldığını anlatan yazılar karşılık getirilirse programa bakan kişiler daha iyi anlamlandırır. #define PERSONEL_SAYISI 750 int main() { if (x == PERSONEL_SAYISI) ... return 0; } 2. Bir sabitin program içerisinde pekçok yerde kullanıldığı durumlarda değiştirme işlemi tek yerden yapılabilir. Böylece söz konusu program sembolik sabite baglı olarak yazılıp, daha sonra sembolik sabitin değiştirilmesiyle farklı parametrik değerler için çalıştırılabilir. 3. Sayısal sabitlerin kullanılmasında tutarsızlıkları ve yazım yanlışlarını engeller. Örneğin matematiksel hesaplamalar yapan bir kodda sıksık pi sayısını kullandığımızı düşünelim. pi sayısı yerine #define PI 3.14159 sembolik sabitini kullanabiliriz. Her defasında pi sayısının sabit olarak koda girersek, her defasında aynı değeri yazamayabiliriz. Örneğin bazen 3.14159 bazen 3.14156 girebileceğimiz gibi yanlışlıkla 3.15159 da girebiliriz. Sembolik sabit kullanımı bu tür hataları ortadan kaldırır. 4. Sembolik sabitler kullanılarak C sintaksında küçük değişiklikler yapılabilir. #define FOREVER for(;;) #define BEGIN { #define END } Böyle bir sembolik sabit kullanımını kesinlikle tavsiye etmiyoruz. Okunabilirliği artırması değil azaltması söz konusudur. Bir C programcısı için en okunabilir kod C'nin kendi sentaksının kullanıldığı koddur. #define komutu kaynak kodun herhangi bir yerinde kullanılabilir. Ancak tanımlandığı yerden kaynak kodun sonuna kadar olan bölge içerisinde etki gösterir. Ancak en iyi tanımlama yeri kaynak kodun tepesidir. Geleneksel olarak sembolik sabitler #include satırlarından (bir satır boşluk verildikten sonra) hemen sonra yer alırlar. Sembolik Sabitlerin Kullanılmasında Çok Yapılan Hatalar 1. Sembolik sabit tanımlamasında gereksiz yere = karakterini kullanmak. #define N = 100 /* yanlış */ Bu durumda önişlemci N gördüğü yere = 100 yapıştıracaktır. Örneğin int a[N] gibi bir dizi tanımlanmışsa önişlemci bu tanımlamayı a[N = 100] yapar ki bu da derleme aşamasında hata (error) ile neticelenir. 2. Sembolik sabit tanımlama satırını ; ile sonlandırmak. #define N 100; /* yanlış */ Bu durumda önişlemci N gördüğü yere 100; yerleştirir. int a[N]; tanımlaması int a[100;] haline gelir. Derleme aşamasında hata oluşur. (Borland derleyicileri hata mesajı :Array bounds missing ])Bu tür hatalarda derleyici sembolik sabit kaç yerde kullanılmışsa o kadar error verecektir. 3. Sembolik sabitlerle yer değiştirecek ifadelerin operatör içermesi durumunda bu ifadeleri paranteze almadan yerleştirmek. #define SIZE 5 + 10 ... i = SIZE * 5 /* 5 + 10 * 5 = 55 (75 değil) */
21 Haziran 2020 Pazar
C programlama ön işlemci kavramları
Kaydol:
Kayıt Yorumları (Atom)
Hiç yorum yok:
Yorum Gönder
Her yorum bilgidir. Araştırmaya devam...