C Programlama

Ek Bilgiler

C'de Önişlemci Direktifleri

► Detaylı anlatım

Derleme işleminin ilk safhasında, önişlemci derleyiciye bazı direktifler ulaştırır. Derleyici tarafından bazı özel işlemlerin yapılmasını sağlayan bu direktiflere Önişlemci Direktifleri adı verilir. Şimdi, önişlemci direktiflerini sıra ile incelemeye çalışalım:

#define Önişlemci Direktifi

#define önişlemci direktifinin genel yapısı aşağıda yer almaktadır:

#define makro-adı karakter-dizisi

Yukarıdaki satırı programınızın başında kullandığınız zaman, program makro-adı ifadesi ile gösterilen makro adını gördüğü her yerde karakter-dizisi ifadesi ile gösterilen karakter dizisi yazılmış gibi hareket eder. #define önişlemci direktifinin tanımlandığı satır normal işlem satırları gibi noktalı virgül işareti ile sona ermez.

Kullandığınız makro adlarını küçük veya büyük harflerle tanımlamak mümkündür. Programların daha kolay anlaşılması için büyük harf kullanmanız daha faydalı olacaktır. Makro adı ile karakter dizisi arasında bir veya birden fazla boşluk bırakabilirsiniz.

Şimdi, biraz daha ayrıntılı bir örneği incelemeye çalışalım:

#include <stdio.h>

#define MAK01 "Bir int değer giriniz: "
#define MAK02 20

void main (void)
{
  int id1;

  printf(MAK01);              /* 1 */
  scanf("%d", &id1);

  printf("%d", MAK02+id1);    /* 2 */
}

Yukarıdaki örnekte, program MAK01 ve MAK02 adlı 2 makro tanımlar. MAK01 makrosu "Bir int değer giriniz: " , MAK02 makrosu ise 20 karakter dizisini temsil eder. Program, 1 sayısı ile gösterilen işlem satırında MAK01 ifadesi ile karşılaştığı her yerde, makronun temsil ettiği karakter dizisini yerleştirdiği için "Bir int değer giriniz:" karakter dizisini ekrana yazar. Girdiğiniz int değeri id1 değişkenine atar. 2 sayısı ile gösterilen işlem satırında ise MAK02 makrosunun karşılığı olan 20 değeri ile id1 değişken değerini toplayarak ekrana yazar. Başlangıçta bir karakter dizisi olarak kabul edilen 20 ifadesini sayısal bir değere çeviren C derleyicisidir.

Makro işlemlerinde, #define satırında tanımlanan ve program içinde makro adının yerine kullanılan ifade ister karakter ister sayısal değerler içersin, daima bir karakter dizisi olarak kabul edilir.

#define önişlemci direktifi ile tanımladığınız makroları ister bütün fonksiyonların dışında ister herhangi bir fonksiyonun içinde tanımlayın, makro bir kez tanımlandıktan sonra, bütün fonksiyonlar dahil olmak üzere programın herhangi bir yerinde kullanabilirsiniz. Bu özelliği bir örnek üzerinde incelemeye çalışalım:

#include <stdio.h>

void fonk1 (void);
void fonk2 (void);

#define MAK01 5

void main (void)
{
  #define MAK02 10

  int id1;

  for (id1=0; id1<MAK01; id1++) printf("%d ", id1+1);
  printf("\n");
  fonk1();
  fonk2();
}

void fonk1 (void)
{
  #define MAK03 15

  int id1;

  for (id1=0; id1<MAK02; id1++) printf("%d ", id1+1);
  printf("\n");
}

void fonk2 (void)
{
  int id1;

  for (id1=0; id1<MAK03; id1++) printf("%d ", id1+1);
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

1 2 3 4 5
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Program, bütün fonksiyonların dışında MAK01 adlı, main() fonksiyonunda MAK02 adlı ve fonk1() fonksiyonu içinde MAK03 adlı bir makro oluşturur. Dikkat ederseniz program, tanımlandıkları yerden farklı olarak, MAK01 makrosunu main() fonksiyonu içinde, MAK02 makrosunu fonk1() fonksiyonu içinde ve MAK03 makrosunu fonk2() fonksiyonu içinde kullanır.

#include Önişlemci Direktifi

C dilinde, daha önce değindiğimiz gibi, en çok kullanılan fonksiyonlar kütüphane dosyaları adı verilen .LIB uzantılı dosyalara yerleştirilmiştir. Bu dosyalarda yer alan kütüphane fonksiyonlarının prototipleri ise başlık dosyası adı verilen .H uzantılı dosyalarda yer alır.

#include direktifini kullanarak, başlık dosyalarında prototipleri yer alan fonksiyonları sizin programınızda yazılmış gibi kullanabilirsiniz.

Aşağıdaki satırı programınıza dahil ederseniz, conio.h başlık dosyası içinde prototipleri bulunan bütün kütüphane fonksiyonlarını, programınızda yazılmış gibi kullanabilirsiniz:

#include <conio.h>

Eğer dosya adını < > işaretleri arasında tanımlarsanız, derleyici tanımladığınız dosyayı başlık dosyalarının içinde bulunduğu alt dizinde arar. Bu dizin genellikle include alt dizinidir. Eğer dosya adını " " işaretleri arasında tanımlarsanız, derleyici tanımlanan dosyayı önce aktif dizinde, daha sonra diğer dizinlerde arar. " " işaretlerini kullandığınız zaman, eğer varsa, derleyici tanımladığınız dosyayı mutlaka bulur. Eğer sisteminizi uygun şekilde düzenlediyseniz, < > işaretlerini kullanmanız derleme zamanını kısaltır.

Şimdi, #include önişlemci direktifinin kullanılmasını örnekler üzerinde incelemeye çalışalım:

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

void main (void)
{
  int id1;

  printf("Bir karakter giriniz: ");
  id1 = getch();

  printf("\n%c : ASCII kodu %d", id1, id1);
}

Yukarıdaki örnekte, program başlık dosyalarının birini < >, diğerini ise " " işaretleri arasında tanımlamaktadır.

Koşula Bağlı Derleme

C dilinde yazdığınız programların bölümlerini seçeneğe bağlı olarak derleyebilirsiniz. Bu işleme Koşula Bağlı Derleme adı verilir. Koşula bağlı derleme aşağıdaki direktifler tarafından gerçekleştirilir:

#if
#else
#elif
#endif
#ifdef
#ifndef

#if direktifinin genel yapısı aşağıda gösterildiği şekildedir:

#if sabit-ifade
   işlemler
#endif

Burada, sabit-ifade değeri doğru sonuç verirse #if ile #endif satırı arasında kalan bütün işlem satırları derlenir, aksi takdirde derleyici bu satırları derlemeden geçer. Derlemenin ilk safhası önişlem safhası olduğu için burada bahsi geçen sabit-ifade yerine herhangi bir değişken değeri kullanamazsınız. #if ve #endif direktiflerinin kullanılmasını örneklerle incelemeye çalışalım:

#include <stdio.h>

#define MAK01 20
#define MAK02 50

void main (void)
{
  #if MAK01<MAK02
     printf("MAK01 değeri MAK02 değerinden küçüktür.\n");
     printf("MAK01 değeri: %d\nMAK02 değeri: %d\n", MAK01, MAK02);
  #endif

  #if MAK01>MAK02
     printf("MAK01 değeri MAK02 değerinden büyüktür.\n");
     printf("MAK01 değeri: %d\nMAK02 değeri: %d\n", MAK01, MAK02);
  #endif
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

MAK01 değeri MAK02 değerinden küçüktür.
MAK01 değeri: 20
MAK02 değeri: 50

Yukarıdaki örnekte, program , MAK01 değeri MAK02 değerinden küçük olduğundan, sadece ilk #if direktifi içinde yer alan satırları derler. İkinci #if direktifi içinde yer alan işlem satırları hiç derlenmez.

#if direktifi ile birlikte #else direktifini kullanabilirsiniz. #else direktifinin genel yapısı aşağıda gösterildiği şekildedir:

#if sabit-ifade
   işlemler
#else
   işlemler
#endif

Eğer, #if satırında yer alan sabit-ifade doğru sonuç verirse, #if direktifi ile ilgili işlem satırları, aksi takdirde #else direktifi ile ilgili işlem satırları derlenir. Bu özelliği örnekler üzerinde incelemeye çalışalım:

#include <stdio.h>

#define MAK01 1

void main (void)
{
  int id1;

  #if MAK01 == 1
     for (id1=0; id1<10; id1++) printf("%d ", id1+1);
  #else
     for (id1=10; id1>0; id1--) printf("%d ", id1);
  #endif
}

Yukarıdaki örnekte, program aşağıdaki satırı ekrana yazar:

1 2 3 4 5 6 7 8 9 10

Program, #if satırındaki ifade doğru sonuç verdiği için, sadece #if direktifi ile ilgili işlem satırını derler ve 1'den 10'a kadar olan sayıları ekrana yazar. Diğer işlem satırları hiç derlenmez.

Aşağıda genel yapısı verilen #elif direktifini kullanarak bir if-else-if merdiveni oluşturabilirsiniz:

#if sabit-ifade1
   işlemler
#elif sabit-ifade2
   işlemler
#elif sabit-ifade3
   işlemler
.
.
.
#endif

if-else-if merdiveninde doğru olan ilk ifadenin yer aldığı #if veya #elif satırına bağlı olan işlem satırları derlenir, diğer işlem satırları derleyici tarafından dikkate alınmaz. if-else-if merdiveninin kullanılmasını bir örnek üzerinde incelemeye çalışalım:

#include <stdio.h>

#define MAK01 3

void main (void)
{
  int id1;

  #if MAK01 == 1
    for (id1=1; id1<=10; id1++) printf("%d ", id1);
  #elif MAK01 == 2
    for (id1=2; id1<=10; id1++) printf("%d ", id1);
  #elif MAK01 == 3
    for (id1=3; id1<=10; id1++) printf("%d ", id1);
  #endif
}

Yukarıdaki örnekte, program aşağıdaki satırı ekrana yazar:

3 4 5 6 7 8 9 10

MAK01 makrosuna 1 ile 3 arasındaki sayılardan hangisini atarsanız, program o sayıdan başlamak suretiyle 10'a kadar olan sayıları ekrana yazar.

#ifdef direktifinin genel yapısı ise aşağıda gösterildiği şekildedir:

#ifdef makro-adı
   işlemler
#endif

Eğer makro adı tanımlanırsa, derleyici #ifdef direktifi ile ilgili işlem satırlarını derler. Aksi takdirde, söz konusu işlem satırlarını dikkate almaz. #ifdef direktifi ile birlikte #else direktifini kullanmanız mümkündür.

#ifndef direktifinin genel yapısı da tıpkı #ifdef direktifi gibidir. Tek fark #ifndef direktifi ile ilgili işlem satırlarının sadece makro adı tanımlanmadığında derlenmesidir.

Şimdi, bu özellikleri örnekler üzerinde incelemeye çalışalım:

#include <stdio.h>

#define MAK01 4

void main (void)
{
  int id1;
  
  #ifdef MAK01
     int id2=0;
  #endif

  for (id1=0; id1<10; id1++) {
       printf("%d ", id1+1);

       #ifdef MAK01
          id2 += MAK01 * (id1+1);
       #endif
  }

  #ifdef MAK01
     printf("\nToplam: %d", id2);
  #endif
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

1 2 3 4 5 6 7 8 9 10
Toplam: 220

Program, 1'den 10'a kadar olan sayıları ve bu sayıların 4 ile çarpımlarının toplamını ekrana yazar. id2 değişkeni ile ilgili işlem satırlarının derlenerek çalışmasının nedeni, programda MAK01 adlı bir makronun tanımlanmış olmasıdır.

#include <stdio.h>

void main (void)
{
  int id1;
  
  #ifndef MAK01
     int id2=0;
  #endif

  for (id1=0; id1<10; id1++) {
       printf("%d ", id1+1);

       #ifndef MAK01
          id2 += 4 * (id1+1);
       #endif
  }

  #ifndef MAK01
     printf("\nToplam: %d", id2);
  #endif
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

1 2 3 4 5 6 7 8 9 10
Toplam: 220

Program, 1'den 10'a kadar olan sayıları ve bu sayıların 4 ile çarpımlarının toplamını ekrana yazar. id2 değişkeni ile ilgili işlem satırlarının derlenerek çalışmasının nedeni, programda MAK01 adlı bir makronun tanımlanmamış olmasıdır.

Kısaca özetlemek gerekirse, #ifdef direktifi içinde yer alan işlem satırlarının derlenmesi için direktif içinde geçen makronun mutlaka program içinde tanımlanmış olması, #ifndef direktifi ile ilgili işlem satırlarının derlenmesi için ise, direktif içinde geçen makronun tanımlanmamış olması gerekir.

#error, #undef, #line ve #pragma Önişlemci Direktifleri

#error direktifinin genel yapısı aşağıda gösterildiği şekildedir:

#error hata-mesajı

#error direktifi derleyicinin çalışmasını durdurur ve hata ile ilgili bilgileri içeren hata mesajını verir. Derleyici bir dosya içinde #error ifadesini gördüğünde derleme işlemi sona erer. #error direktifini bir örnek üzerinde incelemeye çalışalım:

#include <stdio.h>

void main (void)
{
  int id1;

  printf("Bir int değer giriniz: ");
  scanf("%d", &id1);

  #error Programda hata var! /* 1 */
  printf("%d", id1);
}

Yukarıdaki örnekte yer alan program derleyici tarafından derlenmez. Derleyici 1 sayısı ile gösterilen işlem satırındaki #error direktifini görür görmez, derleme işlemini sona erdirir.

#undef direktifinin genel yapısı aşağıda gösterildiği şekildedir:

#undef makro-adı

#undef direktifi tanımlanmış bir makro adını geçersiz hale getirir. Yani, #undef direktifi ile geçersiz hale getirilen bir makroya, derleyici program tarafından daha önce tanımlanan makroya hiç tanımlanmamış gibi işlem yapar. Eğer, makro adı tanımlanmamışsa #undef direktifinin herhangi bir etkisi olmaz.

Şimdi, #undef direktifinin kullanılmasını bir örnek üzerinde incelemeye çalışalım:

#include <stdio.h>

#define MAK01 21

void main (void)
{
  #undef MAK01  /* 1 */

  #ifdef MAK01
     printf("MAK01 değeri: %d ", MAK01);
  #endif

  #ifndef MAK01
     printf("MAK01 makrosu tanımlanmamıştır!");
  #endif
}

Yukarıdaki örnekte, program aşağıdaki satırı ekrana yazar:

MAK01 makrosu tanımlanmamıştır!

Program, MAK01 makrosunu main() fonksiyonundan önce tanımlar. Ancak 1 sayısı ile gösterilen işlem satırında MAK01 makro tanımlaması #undef direktifi ile geçersiz hale getirildiğinden, #ifdef direktifi ile ilgili işlem satırı derleyici tarafından derlenmez, sadece #ifndef direktifi ile ilgili işlem satırı derlenir.

#line direktifinin genel yapısı aşağıda gösterildiği şekildedir:

#line satır-numarası "dosya-adı"

Bir C derleyicisi, bir dosyayı derlerken 2 çeşit bilgi içerir:

1. Derlenmekte olan satırın numarası
2. Derlenmekte olan kaynak dosyanın adı

#line direktifi bu 2 değeri değiştirmek için kullanılır. Başka bir ifade ile, birazdan inceleyeceğimiz __LINE__ ve __FILE__ makrolarının içeriğini değiştirmeye yarar.

#line direktifinin kullanılmasında, satır-numarası ifadesi dosya içinde derlenecek olan ilk satır numarasını verir. satır-numarası ifadesinin değeri 1 ile 32767 arasında olmalıdır. dosya-adı ifadesi ise derlenen dosya adıdır. Dosya adı, işletim sistemine göre verilen bir dosya adıdır. Dosya adının tanımlanması isteğe bağlıdır.

#include <stdio.h>

#line 40
                          /* 40 */
void main (void)          /* 41 */
{                         /* 42 */
 printf("%d", __LINE__);  /* 43 */
}

Yukarıdaki örnekte, program aşağıdaki değeri ekrana yazar:

43

Program içinde line direktifi kendisinden sonra gelen kod satırının numarasını 40 değerine ayarlar. Bundan dolayı printf() fonksiyon satırında satır numarası 43 olarak ekrana yazılır.

#pragma direktifi, bir derleyiciye ek bilgi göndererek derleyicinin belirli özelliklerini kontrol eden, C standartları tarafından tanımlanmış özel derleyici komutlarıdır. Bu direktifi kullanarak programlara özel direktifler verebilirsiniz. #pragma direktifleri bilgisayar, işletim sistemi ve derleyiciye bağlı olarak değişebilir. Bu direktifin genel yapısı aşağıda gösterildiği şekildedir:

#pragma pragma-adı

pragma-adı ifadesi kullanılacak olan direktifin adını gösterir. Eğer derleyici tanımadığı bir #pragma ifadesi ile karşılaşırsa, onu dikkate almaz.

#pragma direktiflerinin sayısı derleyicilere göre değişebilir.

C İçinde Tanımlı Makrolar

ANSI C standartlarına uygun bütün derleyicilerde en az 5 adet önceden tanımlanmış makro bulunur. Bu makrolar aşağıda gösterilmektedir:

__LINE__ : Derlenmekte olan satırın numarasını taşıyan int bir değerdir.

__FILE__ : Derlenmekte olan dosyanın adını gösteren bir karakter dizisidir.

__DATE__ : Sistemin tarihini gösteren bir karakter dizisidir. 

           Genel yapısı : ay/gün/yıl

__TIME__ : Programın derleme başlangıcındaki zamanı gösteren bir karakter dizisidir.
                                       
           Genel yapısı : Saat:Dakika:Saniye

__STDC__ : Eğer derleyici ANSI standardına uygun ise 1 olarak tanımlanır.

Yukarıda bahsi geçen makroları #undef direktifini kullanarak geçersiz hale getiremezsiniz.

Şimdi, öğrendiklerimizi bir örnek üzerinde incelemeye çalışalım:

#include <stdio.h>

void main (void)
{
  int id1;

  for (id1=0; id1<10; id1++) printf("%d ", id1+1);

  printf("\nDerlenen dosya: %s\n", __FILE__);
  printf("Derlenen satır: %d\n", __LINE__);
  printf("Derleme tarihi: %s\n", __DATE__);
  printf("Derleme zamanı: %s", __TIME__);
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

1 2 3 4 5 6 7 8 9 10
Derlenen dosya: deneme.c
Derlenen satır: 13
Derleme tarihi: Dec 3 2011
Derleme zamanı: 21:37:54

Program, 1'den 10'a kadar olan sayıları ekrana yazar. Sonra, derlenen dosya adını, derlenen satır sayısını, derleme tarihini ve zamanını ekrana yazar. Siz programı derlerken, derleme tarih ve zamanı farklı değerler alacaktır.

# ve ## İşlemcileri

# işlemcisi, fonksiyona benzeyen makroların argümanını tırnak içinde bir karakter dizisi olarak geri verir.

## işlemcisi 2 tanımlayıcıyı birleştirir.

# ve ## işlemcilerinin kullanılmasını örnekler üzerinde incelemeye çalışalım:

#include <stdio.h>

#define MAK01(id1) #id1

void main (void)
{
  int id2;

  id2 = 21;
  printf("%s değişken değeri: %d", MAK01(id2), id2); /* 1 */
}

Yukarıdaki örnekte, program aşağıdaki satırı ekrana yazar:

id2 değişken değeri: 21

Program, 1 sayısı ile gösterilen işlem satırında bulunan printf() fonksiyonundaki MAK01(id2) ifadesini %s format tanımlayıcısının yerine koyar. Bu işlemi gerçekleştiren program başında tanımlanan makrodur.

#include <stdio.h>

#define MAK01(id1) printf("%d", (id1 ## 1) + (id1 ## 2))

void main (void)
{
  int id1, id2;

  id1 = 12;
  id2 = 25;

  MAK01 (id); /* printf("%d", id1+id2); */
}

Yukarıdaki örnekte, program aşağıdaki satırları ekrana yazar:

37

Program, MAK01 makrosu için argüman olarak verilen ifadeye 1 ve 2 karakterlerini ayrı ayrı ekleyerek elde ettiği 2 değişken toplamını ekrana yazar.