C Programlama Dili'ne Giriş

  Dersler
* Önsöz

* Giriş
* Veri Tipleri, Değişkenler
* Operatörler
* Temel G/Ç Fonksiyonları
* Temel Kütüphane Fonksiyonları
* Karşılaştırma Deyimleri
* Döngüler
* Fonksiyonlar I
* Fonksiyonlar II
* Diziler
* Gösterici (Pointer) Kavramı
* Katarlar (Stringler)
* Dinamik Bellek Yönetimi
* Gösterici Uygulamaları
* Yapılar ve Birlikler
* Dosya Yönetimi
* Bit Düzeyinde Çalışmak
* Port Denetimi
* Grafik Kullanımı
* C Makroları

* Kısaca C++
* Derleme Seçenekleri
* Tarih-Saat Fonksiyonları
* Monte-Carlo Yöntemleri
* Fortran ve C

* Yararlanılan Kaynaklar

  C/C++ Derleyicileri
* Dev-C++
* Salford (silversoft FTN95)
* GCC
* Turbo C
* Eclipse IDE
* NetBeans IDE

  Dış Bağlantılar
* programlama.com
* C Programcıları Derneği

* C (wikipedia)
* C++ (wikipedia)
* cplusplus.com
* koders.com
* Hot scripts

 

Ders 20: C Makroları

###################- (%95)

En son güncelleme: Wed, 30 Nov 2011 13:22:02 +0200

Giriş

Bir C (veya C++) programlama dilinde, program başında diyez ('#') işareti ile başlayan satırlar geçekte C (veya C++) diline ait olmayıp ön işlemci dilidir. Bu yüzden derleme işlemleri iki adımda yapılır. Daha ayrıntılı bilgi için bkz: Bölüm 22.

Makro bildirimleri veya Yönergeleri (direktive) derleme öncesi komutlarıdır. Bunlar tipik olarak:

  • programları değiştirmek
  • program parçalarını kaynak programında birleştirmek
  • derleme sırasında bazı uyarı mesajlarını aktif veya pasif hale getirmek
için kullanılır. Genelde makro bildirimleri kaynak dosyaların en başında verilir.

C dilinde kullanılan Yönergeler (önişlemci komutları) şunlardır:

   #include    #define     #pragma
   #error      #undef      #ifdef      #ifndef
   #if         #else       #elif       #endif

20.1   #include Yönergesi

Bu önişlemci verilen dosyanın içeriğini, kullanıldığı yerde kaynak dosyasının içine ekler. Çoğunlukla derleyiciye ait komut kütüphanelerinde bulunan fonksiyonların prototiplerinin ve diğer çeşitli tanımlamaların bulunlunduğu (h uzantılı) başlık dosyalarının programa dahil edilmesinde kullanılır[2]. İki tür kullanımı vardır:

      #include <dosya_adı.h>
veya
      #include "dosya_adı.h"
  • Birinci kullanımda dosyanın nerede bulunduğu derleyici için verilen ulaşım yolu ile belirlenir. Bu yol genellikle include dizini ile son bulur. Başlık dosyalarının saklandığı include dizini
    • Borland firmasına ait Turbo C derleyicisinde : C:\TC\INCLUDE
    • Linux ortamında : /usr/include şeklindedir.
  • İkinci kullanımlada dosyanın bulunduğu yer aktif dizin olarak kabul edilir. Aksi halde yol tam olarak verilmelidir.

#include deyimi ile program ilave edilecek dosya C fonksiyonları içerebileceği gibi basit deyimler de içerebilir. Bunun için bir sınırlandırma yoktur. Hatta uzantıları .h olması bile gerekmez. Program 20.1 ve Program 20.2'yi inceleyin.

Program 20.1: #include önişlemcisinin kullanımı için bir örnek
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
/* 20prg01.c: faktoriyel ve kombinasyon hesaplamaları */

#include <stdio.h>

#include "komb.h"

int main()
{
    int i;

    /* 0 dan 10 yekadar olan sayıların faktoriyelleri */
    for(i = 0; i<=10; i++)
       printf("%2d! = %d\n",i,faktoriyel(i));

    /* 10'un 1li, 2li, ... kombinasyonları */
    for(i = 0; i<=10; i++)
       printf("C(10,%2d) = %d\n",i,C(10,i));

 return 0;
}

ÇIKTI

 0! = 1
 1! = 1
 2! = 2
 3! = 6
 4! = 24
 5! = 120
 6! = 720
 7! = 5040
 8! = 40320
 9! = 362880
10! = 3628800
C(10, 0) = 1
C(10, 1) = 10
C(10, 2) = 45
C(10, 3) = 120
C(10, 4) = 210
C(10, 5) = 252
C(10, 6) = 210
C(10, 7) = 120
C(10, 8) = 45
C(10, 9) = 10
C(10,10) = 1

Program 20.1'e 5.satırda #include önişlemcisi ile komb.h adlı başlık dosyası eklenmiştir. komb.h faktoriyel ve kombinasyon işlemleri için fonksiyonlar barındırır. Bu dosyanın içeriği şöyledir:

/* komb.h
   Kombinasyon işlemleri ile ilgi fonksiyonlar
*/

/* n! sayısını gönderir */
int faktoriyel(int n){
  int i, f;

   for(f=1, i=2; i<=n; i++)
      f *= i;

   return f;
}

/* n'nin r'li kombinasyonunu hesaplar */
int C(int n, int r){
  return ( faktoriyel(n)/(faktoriyel(r)*faktoriyel(n-r)) );
}

Program 20.2: #include önişlemcisinin kullanımı için başka bir örnek
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
/* 20prg02.c: 
   Klavyeden girilen bir doğal sayının, kaç basamaklı 
   olduğunu bulup ekrana yazar.
*/

#include <stdio.h>


int main(){

  #include "bildirim.inc"

  printf("Bir dogal sayi gir: ");
  scanf("%d",&sayi);


  if(sayi<0) return BASARISIZ;

  b = 0;
  n = sayi;
 
  while(n>0){
    n /= 10;
    b++;
  }

  printf("%d  %d basamakli bir sayidir.\n",sayi,b);

  return BASARILI;
}

Dikkat edilirse Program 20.2 içinde değişkenler tanımlanmamıştır. 11. satırdaki bildirim.inc dosyası değişken bildirimlerini barındırmaktadır. Bu özellik (veya esneklik), çok büyük ve profesyonel programlarda kullanılmaktadır.

ÇIKTI

Bir dogal sayi gir: 4578
4578  4 basamakli bir sayidir.


20.2   #define Yönergesi

Bu önişlemci komutu, kaynak dosyada bir isim yerine başka bir isimin yerleştirilmesini sağlar. Programda kullanılan bu sembolik isimler başta ana program olmak üzere bütün alt programlarda da aynı değere sahiptir. Yani #define önişlemcisi ile tanımlanan her ne olursa olsun, tanımlama bütün fonksiyonlarda kullanılabilir. Bir çeşit genel (global) bildirim gibi davranır. Örneğin:

Program 20.3: #define önişlemcisinin kullanımı
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
/* 20prg03.c: #define önişlemcisinin kullanımı */

#include <stdio.h>

#define PROGRAM main()
#define BASLA   {
#define BIT     }
#define YAZ     printf

PROGRAM
BASLA
   YAZ("Merhaba C!..\n");
BIT

Program 20.3 derleme işleminden önce #define ile verilen ilk sembolik isimler yerine ikinci isimler yerleştirildikten sonra program aşağıdaki durmuma gelir:

  /* 20prg03.c: #define önişlemcisinin kullanımı */

  #include <stdio.h>

  main()
  {
    printf("Merhaba C!..\n");
  }

Bu önişlemciyi kullanak sembolik sabitler tanımlamak mümkündür. Örneğin:

  #define PI      3.1415926
  #define IKI_PI  2.0*PI
  #define YUZ     100
gibi.

#define önişlemcisinin kullanımı için iyi bir örnek Program 20.4 de verilmiştir. Program km/s biriminde verilen bir hızı m/s birimine çevirir[4].

Program 20.4: #define önişlemcisinin kullanımı
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
 /* 20prg04.c: km/s biriminde verilen hızı m/s cinsinden hesaplar */

 #include <stdio.h>

 #define km   *1000.0
 #define saat *3600.0

 main()
 {
    double yol,zaman,hiz;

    yol   = 100 km;
    zaman = 1.2 saat;

    hiz = yol/zaman;

    printf("HIZ = %lf m/s\n",hiz);

 }

ÇIKTI

HIZ = 23.148148 m/s

5. ve 6. satırda tanımlanan sembolik sabitler km ve saat program içinde kullanıldığında sol taraflarındaki sayıyı sırasıyla 1000 ve 3600 ile çarparlar. 12. satırdaki yol değişkenine 100*1000.0 değeri atanır. Benzer olarak 13. satırdaki zaman değişkenine 1.2*3600.0 sayısı atanır. Dikkat edilirse sembolik sabitler kullanıldığında programın okunurluğu artmakta ve bundan dolayı hata ayıklama kolaylaşmaktadır.

#define önişlemcisi ile parametrik tanımlamalar veya global fonksiyonlar tanımlamak mümkün olur. Örneğin:

Program 20.5: Makro fonksiyon tanimlama
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
/* 20prg05.c: Makro fonksiyon tanimlama. */

#include <stdio.h>
#include <math.h>

/* makro fonksiyonlar */
#define kare(x)   (x*x)
#define topl(x,y) (x+y)
#define carp(x,y) (x*y)
#define hipo(x,y) sqrt(x*x+y*y)

main(void)
{
   float a=3.0, b=4.0;

   printf("kare(2)   = %f\n",kare(2));
   printf("topl(a,b) = %f\n",topl(a,b));
   printf("carp(a,b) = %f\n",carp(a,b));
   printf("hipo(a,b) = %f\n",hipo(a,b));
}

ÇIKTI

kare(2)   = 4.000000
topl(a,b) = 7.000000
carp(a,b) = 12.000000
hipo(a,b) = 5.000000

Programda tanımlanan kare(2) ifadesi (2)*(2) şeklinde yorumlar. Benzer durum diğer makrolar için de geçerlidir.

Makrolar C'de çok sık kullanılır. Örneğin, tek boyutlu bir dizinin boyutu öğrenilmek istendiğinde aşağıdaki makro kullanılabilir:

   #define BOYUT sizeof(DIZI)/sizof(DIZI[0])
   ...
   int a[10], n;
   ...
   n = BOYUT(a);

Son satırdaki işlemle, n değişkeninine (a dizisinin boyutu) 10 değeri atanır.

İşte ilginç bir makro daha. Daha önce anlatılan takas(a,b) fonksiyonu gösterici kullanmadan aşağıdaki makro ile yazılabilir:

   #define takas(x,y) {g=(x); (x)=(y); (y)=g;}
   ...
   int x=22, y=33, g;       /* g geçici bir değişken */
   ...
   printf("%d %d\n",x,y);   /* 22 33 */

   takas(a,b)            

   printf("%d %d\n",a,b);   /* 33 22 */
   ...

20.3   #undef Yönergesi

#define ile tanımlanan bir isim, orjinal tanımlamaları kaldırmaksızın farklı değerler için tekrar tanımlanamaz.

   #define SIFRE 14576  /* ilk tanimlama */
    ...
   #define SIFRE 22357  /* hata! tanımlama tekrarlandı. */

Eğer #define ile tanımlanan bir ifade yeniden tanımlanmak istenirse, #undef önişlemcisi ile önceki tanımlama iptal edildikten sonra #define ile yenisi değiştirilir. Yani:

   #define SIFRE 14576  /* ilk tanimlama */
    ...
   #undef  SIFRE        /* ilk tanımlamayı iptal et */
   #define SIFRE 22357  /* yeni tanımlama */

20.4   #if, #elif, #else ve #endif Yönergeleri

Bu önişlemciler, makro düzeyinde kontrol deyimleridir. Genel kullanım biçimi:

   #if (ifade1)
          tanımlama blogu1
   #elif (ifade2)
          tanımlama blogu2
   ...
   #else
          tanımlama bloguN
   #endif
şeklindedir. Burada:
  • #if makrosu if deyimine
  • #elif makrosu else if deyimine
  • #else makrosu else deyimine
  • #endif makrosu if deyiminin sonuna

karşılık gelmektedir. Bu makrolar, donanıma veya işletim sistemine uygun olarak değişik makroların tanımlanmasına izin verir. Örneğin:

Program 20.6: Kontrol önişlemcilerinin kullanımı
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
/* 20prg06.c: Kontrol ön işlemcilerinin kullanımı */

#include <stdio.h>

#if(sizeof(int)==2)
  #define ISLETIM_SISTEMI  "16 bitlik isletim sistemi."
#else
  #define ISLETIM_SISTEMI  "32 bitlik isletim sistemi."
#endif

int main()
{
   printf(ISLETIM_SISTEMI);

return 0;
}

ÇIKTI

32 bitlik isletim sistemi.

Bu program eski DOS işletim siteminde derlenip çalıştırıldığında, program çıktısı şöyle olur:

ÇIKTI

16 bitlik isletim sistemi.


20.5   #ifdef ve #ifndef Yönergeleri

  • #ifdef önişlemcisi ile, bir ismin tanımlanmış olup olmadığı
  • #ifndef önişlemcisi ile, bir ismin tanımlanmamış olup olmadığı
sorugulanır. Örneğin:

   #ifndef SIFRE
      #define SIFRE 22357
   #endif
gibi.

Program 20.7: Tanımlanmış ise pi sayısını kullanır.
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
/* 20prg07.c: Tanımlanmış ise PI sayısını kullanır */

#include <stdio.h>
#include <math.h>

#define PI 3.141593

main()
{
   double c, r = 21.3;

    #ifdef PI
       c = 2.0 * PI * r;
       printf("Dairenin cevresi = %lf\n",c);
    #else
       printf("PI saysisi tanimlanmamis.\n");
    #endif
}

ÇIKTI

Dairenin cevresi = 133.831862


20.6   #error Yönergesi

Önişlemci bu deyimle karşılaşınca yanındaki mesajı ekrana yazar ve derleme işlemine son verir. Mesela, yazmış olduğunuz program 32 bitlik bir işletim sistemi (WINDOWS veya Linux gibi) için tasarlanmışsa ve program 16 bitlik işletim sisteminde (MSDOS gibi) derlenecekse kullanıcıya buna dair bir uyarı mesajı vermek uygun olur[2-4]. Örneğin:

   #if (sizeof(int)==2)
      #error Bu program 16 bitlik işletim sisteminde derlenemez !...
   #endif

Eğer DOS altında çalışıyorsanız önişlemci derleme işlemine:

    Bu program 16 bitlik işletim sisteminde derlenemez !...
mesajı ile son verir. Mesajın tırnak içine alınmadığına dikkat ediniz.


20.7   Önceden Tanımlanmış Sembolik Sabitler

Bazı sembolik sabitler derleyici tarafından önceden tanımlanmıştır. Bu sabitlerden bazıları Tablo 20.1 de verilmiştir.

Tablo 20.1: Önceden tanımlı bazı sembolik sabitler
Sabit ismi Açıklama
__LINE__ Önişlemci bu sabit yerine kaynak koddaki o anda bulunan satır numarasını yerleştirir.
__FILE__ Kaynak dosyanın ismin tutar.
__DATE__ Önişlemci bu sabit yerine derlemenin yapıldığı zaman tarihi (ay gün yıl formatında) yazar.
__TIME__ Önişlemci bu sabit yerine derlemenin yapıldığı zaman zamanı (sa:dak:sn gün yıl formatında) yazar.
__STDC__ C dilinde kullanılan kimi anahtar sözcükler standart değildir.
Derleyici eğer yalnızca standart C'nin anahtar sözcüklerini destekliyorsa bu sabit tanımlı varsayılır.
M_PI Pi sayısını tutar (M_PI = 3.14159265358979323846). Ayrıca bkz: math.h
M_E e sayısını tutar (M_E = 2.7182818284590452354). Ayrıca bkz: math.h
RAND_MAX Rastgele sayı üretec fonksiyonu rand() ile döndürlen en büyük sayıyı tutar.
(32 bit işletim sitemi için: RAND_MAX = 2147483647). Ayrıca bkz: stdlib.h

Aşağıdaki örnekleri inceleyiniz:

Program 20.8: C dilindeki bazı tanımlı sabitler
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
/* 20prg08.c: Sembolik sabitler */

#include <stdio.h>

main()
{
   printf("Satir No   : %d\n",__LINE__);
   printf("Dosya adi  : %s\n",__FILE__);
   printf("Tarih      : %s\n",__DATE__);
   printf("Saat       : %s\n",__TIME__);
}

ÇIKTI

Satir No   : 7
Dosya adi  : 20prg08.c
Tarih      : Sep 21 2008
Saat       : 01:58:56

Program 20.9: C dilindeki bazı tanımlı sabitler
01: 
02: 
03: 
04: 
05: 
06: 
07: 
08: 
09: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
/* 20prg09.c: Sembolik sabitler */

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

#ifndef __STDC__
  #error Bu derleyici ANSI C degil.
#endif

#ifndef RAND_MAX
  #error RAND_MAX tanımlı degil.
#endif

main()
{
   double r = (double) rand()/RAND_MAX;
   double ikiPi = 2.0*M_PI;
   double birBoluE = 1.0/M_E;

   printf("r  = %lf\n",r);
   printf("ikiPi = %lf\n",ikiPi);
   printf("birBoluE = %lf\n",birBoluE);
}

ÇIKTI

r  = 0.840188
ikiPi = 6.283185
birBoluE = 0.367879



Powered by PHP