[LinuxFocus-icon]
Ev  |  Erişimdüzeni  |  İçindekiler  |  Arama

Duyumlar | Belgelikler | Bağlantılar | LF Nedir
Bu makalenin farklı dillerde bulunduğu adresler: English  Castellano  ChineseGB  Deutsch  Francais  Italiano  Portugues  Russian  Turkce  Arabic  

convert to palmConvert to GutenPalm
or to PalmDoc

[Lorne Bailey]
tarafından Lorne Bailey
<sherm_pbody(at)yahoo.com>

Yazar hakkında:

Lorne Şikago da yaşıyor ve Oracle veritabanlarından veri alma-girme konusunda uzmanlaşmış bir bilgisayar danışmanıdır. Unix ortamında programlama yapmaya başladığından beri, Lorne 'DLL Cehennemi' nden tamamıyla kaçınmıştır. Halen Bilgisayar Bilimleri'nde yüksek lisansı için çalışmaktadır.



Türkçe'ye çeviri:
Bülent ÖZDEMİR <bulentozdem(at)hotmail.com>

İçerik:

 

GCC - herşeyin kökü

[Illustration]

Özet:

Bu yazı, sizin C dilinin temellerini bildiğinizi varsayarak derleyici olarak gcc kullanımını size tanıtacaktır. Basit C kaynak kodlarınız için derleyiciyi komut satırından çağırabileceksiniz. Daha sonra, aslında neler olduğuna ve programlarınızın derlenmesini nasıl denetleyebileceğinize hızlıca bir göz atacağız. Bu arada hata ayıklayıcıların kullanımına da çok kısa değineceğiz.

 

GCC Kuralları

Serbest bir yazılımın kapalı ve tescilli bir derleyici ile derlendiğini düşünebilir misiniz? Programınızın çalışan kısmına neler olacağını nereden bilebilirsiniz? Herhangi bir 'arka kapı' veya Truva Atı virüsü olabilir. Ken Thompson, tüm zamanların en büyük hack lerinden birinde, 'login' programının içine bir arka kapı koyan ve derleyici kendisini derlemeye başladığını anladığı zaman kendisini koruyan bir derleyici yazmıştır. Bu tüm zamanların klasiğini, kendi tanımlamasından, bu adresten okuyabilirsiniz. Şanslıyız ki, gcc var. Bir configure; make; make install yaptığınızda, gcc sahne arkasında birçok ağır işi sizin için yapmaktadır. gcc'nin bizim için çalışmasını nasıl sağlarız? Bir kart oyunu yazmaya başlayacağız, ancak derleyicinin fonksiyonlarını gosterecek kadarıyla yetineceğiz. Sıfırdan başlayacağımdan, bir çalıştırılabilir program yaratılabilmesi için derleyicinin neler yapması gerektiğini ve bunları hangi sırada yaptığını anlamamız gerekiyor. Bir C programının nasıl derlendiğine ve gcc'nin bizim istediklerimizi yapması için gereken seçeneklerine bakacağız. Adımlar (ve bu adımları gerçekleştiren araçlar) şunlardır: Ön-derleme (gcc -E), Derleme (gcc), Toplama (as), ve Bağlama (ld).

 

Başlangıçta...

İlk önce, derleyici nasıl çalıştıracağımızı bilmeliyiz. Tüm zamanların klasiği olan ilk C programı ile başlayacağız. (Deneyimliler beni affetmek zorunda).

#include <stdio.h>

int main()
{ printf("Hello World!\n"); }

Bu dosyayı game.c olarak kaydedın. Programı, komut satırında: yazarak derleyebilirsiniz. C derleyicisi, benimsenmiş olarak, a.out isimli çalıştırılabilir program oluşturur. Bunu, şöyle çalıştırabilirsiniz:

a.out
Hello World
Her derleme yaptığınızda yeni a.out eskisinin üzerine yazılacaktır. Bu yüzden, a.out dosyasını hangi programın oluşturduğunu bilemezsiniz. Bu problemı, gcc'ye çalıştırılabilir ismini -o seçeneği ile söyleyerek çözebiliriz. Bu programı game olarak adlandıracağız, ama, C, Java gibi isimlendirme sınırlaması gerektirmediğinden herhangi bir isim de verebiliriz.
gcc -o game game.c
game
Hello World

Bu noktada, çok faydalı bir programa sahip olmaktan hayli uzağız. Bunun kötü birşey olduğunu düşünüyorsanız, derlenmiş ve çalışan bir programımız olduğu gerçeğini anımsayabilirsiniz. Bu programa küçük küçük eklemeler yaptıkça, programın hala çalışır olduğundan emin olmak isteyeceğiz. Öyle görünüyor ki, her yeni başlayan programcı 1,000 satırlık kaynak kod yazıp tek seferde düzeltmeleri yapmak ister. Kimse, evet kimse, bunu yapamaz. Çalışan kücük bir program yazarsınız, değişiklikleri yaparsınız ve tekrar çalışabilir hale getirirsiniz. Bu, tek seferde düzeltmeniz gereken hata sayısını sınırlar. Artı, son olarak ne yaptığınızı bildiğinizden, çalışmama durumunda nereye odaklanacağınızı bilirsiniz. Bu çalışacağını sizin düşündüğünüz, derlenen, ama çalışmayan birşey yapmaktan sizi alıkoyar. Hatırlayın, derlenebilir olması dogru olduğu anlamına gelmez.

Bir sonraki adımımız oyunumuz için bir başlık dosyası oluşturmak. Bir başlık dosyası tek bir yerde, veri tipleri ve fonksiyon tanımlamaları için kullanılır. Bu veri yapılarının tutarlı bir şekilde tanımlanmasını sağlar ki, böylece programın her kısmı herşeyi aynı şekilde tanır.

#ifndef DECK_H
#define DECK_H

#define DECKSIZE 52

typedef struct deck_t
{
  int card[DECKSIZE];
  /* number of cards used */
  int dealt;
}deck_t;

#endif /* DECK_H */

Bu dosyayı deck.h olarak kaydedin. Sadece .c dosyaları derlenebilir, bu yüzden game.c programımızı değiştireceğiz. game.c dosyasının ikinci satırına, #include "deck.h" yazın. Beşinci satıra, deck_t deck; yazın. Herhangi bir yanlış yapmadığımızdan emin olmak için yeniden derleyin.

gcc -o game game.c

Hata yok, problem yok. Derlenmezse, derlenene kadar uzerinde çalışın.

 

Ön-derleme

Derleyici deck_t tipini nereden biliyor? Çünkü ön-derleme sırasında, "deck.h" dosyası "game.c" dosyasına kopyalanır. Kaynak kodun içerisinde önderleyici komutlarının önüne # konulur. gcc'yi -E anahtarı ile kullanarak ön-derleyiciyi çağırabilirsiniz.

gcc -E -o game_precompile.txt game.c
wc -l game_precompile.txt
  3199 game_precompile.txt
Hemen hemen 3200 satır çıktı! Büyük çoğunluğu stdio.h include dosyası, fakat baktığınızda, bizim tanımlamalarımız da orada. Eğer -o anahtarı ile bir çıktı dosyası belirtmezseniz , çıktılar konsola yazılır. Ön-derleme işlemi uç temel amacı gerçekleştirerek kaynak koda büyük esneklik verir:
  1. Derlenecek kaynak dosyasına "#include" dosyalarını kopyalar.
  2. "#define" satırlarını gerçek değerleri ile değiştirir.
  3. Çağırıldıgı yerlerdeki makroları yerine koyar.
Bu, size kaynak kodda kullandığınız isimlendirilmiş sabitlerini (sözgelimi DECKSIZE destedeki kağıt sayısını belirtir) bir yerde tanımlamanızı ve sabitlerin değerini değiştirdiğinizde heryerde otamatik olarak güncellenmesi sağlar. Pratikte -E anahtarını hemen hemen hiç kullanmazsınız, ama bırakın çıktısını derleyiciye aktarsın.

 

Derleme

Bir ara adım olarak, gcc kodunuzu Assembly diline çevirir. Bunu yapmak için, kodunuzu parse ederek ne yapmaya çalıştığınuzu ortaya çıkarması gerekir. Eğer bir yazım(syntax) hatası yapmış iseniz, bunu size söyler ve derleme durur. İnsanlar, hatalı olarak, bu adımı bütün işlemin kendisi gibi algılarlar. Ancak, gcc'nin daha yapacağı çok iş vardır.

 

Toplama

as Assembly kodunu nesne koduna çevirir. Nesne kodu aslında CPU tarafından çalıştırılamaz, ama çalıştışılmaya oldukça yakındır. -c derleyici anahtarı .c dosyasını .o uzantılı bir nesne dosyasına donuşturur. Eğer

gcc -c game.c
komutunu calıştırırsak otamatik olarak game.o isimli bir dosya elde ederiz. Burada önemli bir noktada duralım. Herhangi bir .c dosyasını alarak bir nesne dosyası oluşturabiliriz. Aşağıda göreceğimiz gibi, bu nesne dosyalarını Bağlama adımı ile çalışabilir bir dosyaya toplayabiliriz. Örneğimizle devam edelim. Bir kart oyunu programlıyoruz ve kağıt destesini deck_t ile tanımladık, şimdi kağıtları karıştıracak bir fonksiyon yazacağız. Bu fonksiyon, deste tipine bir işaretçi alacak ve onu, rastgele bir sayı kümesi ile dolduracak. Hangi kartların daha önce kullanildiğini 'drawn' dizisi ile takip edecek. Bu dizi DECKSIZE üyelerinden birini iki kez kullanmamızı engelliyecek.

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "deck.h"

static time_t seed = 0;

void shuffle(deck_t *pdeck)
{
  /* Keeps track of what numbers have been used */
  int drawn[DECKSIZE] = {0};
  int i;

  /* One time initialization of rand */
  if(0 == seed)
  {
    seed = time(NULL);
    srand(seed);
  }
  for(i = 0; i < DECKSIZE; i++)
  {
    int value = -1;
    do
    {
      value = rand() % DECKSIZE;
    }
    while(drawn[value] != 0);

    /* mark value as used */
    drawn[value] = 1;

    /* debug statement */
    printf("%i\n", value);
    pdeck->card[i] = value;
  }
  pdeck->dealt = 0;
  return;
}

Bu dosyayı shuffle.c olarak kaydedin. Kodun içerisine bir hata ayıklama satırı koyduk, öyle ki, program çalıştırıldığı zaman, bu satır üretilen kart numaralarını yazacak. Bu programın fonksiyona litesine herhangi bir katkı da bulunmayacak, ama neler olduğunu görmek açısından önemli. Henüz oyunumuza yeni başladığımızdan fonksiyonumuzun çalışıp çalışmadığını anlamak için başka yolumuz yok. printf satırı sayesinde şu anda tam olark ne olduğunu anlayabiliriz, böylece destemizin doğru dağıtıldığından emin olarak bir sonraki adıma geçebiliriz. Çalıştığından iyice emin olduktan sonra, hata ayıklama satırını programdan çıkartabiliriz. Bu hata ayıklama tekniği çok ham görünebilir, ama çok küçük bir hareketle amacımıza ulaşıyoruz. Daha gelişmiş hata ayıklayıcıları daha sonra tartışacağız.

İki şeye dikkat edin:
  1. Bir parametreyi adresi ile fonksiyona gectik ki, bunu '&' (address of) operatoru sayesinde anlayabilirsiniz. Bu, fonksiyona değişkenin hafızadaki adresini gönderir, böylece fonksiyon değişkenin değerini değiştirebilir. Evrensel değişkenleri de kullanabilirdik, ancak global değişkenler çok nadir kullanılmalıdır. Işaretçiler C dilinin önemli bir parçasıdır, bu yüzden onları iyi anlamanız gerekir.
  2. Yeni bir .c doayasından bir fonksiyon cağırımı yapiyoruz. İşletim sistemi herzaman 'main' isimli fonksiyonu arar ve programı çalıştırmaya oradan başlar. shuffle.c dosyasında 'main' fonsiyonu olmadığından bu programı çalıştıramayız. Bu dosyayı, içerisinde 'main' fonksiyonu olan ve 'shuffle' fonksiyonu çağırımı yapan başka bir program ile birleştirmeliyiz.

gcc -c shuffle.c
komutunu çalıştırın ve shuffle.o dosyası oluştuğundan emin olun. game.c dosyasını açın, ve 7. satırda, deck değişkeni için yapılan deck_t tanımlamasından sonra,
shuffle(&deck);
satırını ekleyin. Şimdi, daha önce yaptığımız gibi çalışan program oluşturmaya çalıştığımız zaman, hata mesajı alacağız:
gcc -o game game.c

/tmp/ccmiHnJX.o: In function `main':
/tmp/ccmiHnJX.o(.text+0xf): undefined reference to `shuffle'
collect2: ld returned 1 exit status
Derleme başarılı oldu, cunku yazim hatamız yoktu. Bağlama adımı başarısız oldu, çünkü derleyiciye 'shuffle' fonksiyonunun nerede olduğunu soylemedik. Bağlama nedir ve derleyiciye bu fonksiyonu nerede bulacağını nasıl söyleyeceğiz?

 

Bağlama

Bağlayıcı,ld, daha once as ile oluşturulmuş nesne kodunu alır ve onu

gcc -o game game.o shuffle.o
komutuyla çalışabilir hale getirir. Bu iki nesneyi biraraya getirir ve game isimli calışabilir dosyayı oluşturur.

Bağlayıcı shuffle fonksiyonunu shuffle.o nesnesinden bulur ve çalışır dosyaya ekler. Nesne dosyalarının güzel tarafı, fonksiyonu tekrar kullanmak istediğimizde, tüm yapmamiz gereken "deck.h" dosyasını eklemek ve shuffle.o nesne dosyasını yeni çalışır dosyanın içine koymaktan ibarettir.

Bunun gibi kodun yeniden kullanımı herzaman karşılaşılan bir durumdur. Hata ayıklama satırı olarak kullandığımız printf fonksiyonunu kendimizin yazmamamyzyn sebebi, bağlayıcı bu fonksiyonun tanımlamasını #include <stdlib.h> dosyasında bulur ve (/lib/libc.so.6) C kütüphanesinde bulunan nesne koduna bağlar. Bu yolla, başkasının yazdığı çalışan bir fonksiyonu kullanıp kendi problemlerimizi çözmekle uğraşabiliriz. Başlık dosyalarının sadece veri ve fonksiyon tanımlamalarını içermesinin sebebi de budur. Normalde, nesne dosyalarını veya kütüphanelerini bağlayıcının çalışır dosyaya koyması için yaratırız. Kodumuzla ilgili bir problem oluşabilir, çünkü başlık dosyamıza herhangi bir fonksiyon tanımlaması koymadik.

 

İki Önemli Anahtar Daha

-Wall anahtari, butun dil yazım ikazlarını, kodumuzun doğru ve mümkün olduğu kadar taşınabilir olduğundan emin olmamiz için etkin hale getirir. Bu anahtarı kullanarak kodumuzu derlediğimizde, şunun gibi birşey görürüz:

game.c:9: warning: implicit declaration of function `shuffle'
Bu, biraz daha işimizin olduğunu bize bildirir. shuffle fonksiyonunun nerede olduğunu derleyiciye bildirmek için header dosyasına bir satır eklememiz gerekiyor, böylece derleyici ihtiyaç duyduğu bütün denetimleri yapabilir. Biraz garip gözüküyor, ama bu bize tanımlamaları gerçekleştirim kısmından ayırmamızı ve fonksiyonumuzu, sadece yeni başlık dosyasına koyup nesne dosyasını birleştirerek istediğimiz yerde kullanmamızı sağlıyor. Şu tek satırı deck.h dosyasına koyacağız.
void shuffle(deck_t *pdeck);
Bu bütün ikaz mesajlarını etkisiz hale getirecek.

Yaygın olarak kullanılan ikinci bir derleyici seçeneği optimizasyondur. -O# (i.e. -O2). Bu derleyiciye hangi seviyede optimizasyon yapacağımızı sçyler. Derleyici, kodun daha hızlı çalışabilmesi için bir çok hünere sahiptir. Bizimki gibi minik bir program için farkı anlamayabiliriz, ama büyük programların farkedilir şekilde hızlı çalışmasını sağlayabilir. Buna heryerde rastlayabilisiniz, o yüzden ne anlama geldiğini bilmeniz gerekiyor.

 

Hata Ayıklama

Hepimizin bildiği gibi, programımızın derlenebiliyor olamsı, onun istediğimiz şekilde çalişacaği anlamına gelmez. Bütün numaraların bir kez kullanıldığından emin olmak için:

game | sort - n | less
komutunu çalıştırıp, eksik birşey olmadığını denetleriz. Bir problem varsa ne yapacağız? Nasıl örtünün altına bakıp, hatalarımızı bulacağız?

Kodunuzu bir hata ayıklayıcı ile kontrol edebilirsiniz. Birçok sürüm klasik hata ayıklayıcı gdb'yi sağlar. Komut satırı seçenekleri benim gibi sizi de mutsuz ediyorsa, KDE'nin KDbg adresinde sunduğu güzel bir önyüzü kullanabilirsiniz. Birbirine çok benzeyen başka önyüzler de vardir. Hata ayıklamaya başlamak için File->Executable seçin ve game programını bulun. F5 e bastiginizda ya da menuden Execution->Run seçtiğinizde, çıktıyı ayrı bir pencerede görmelisiniz. Ne oldu? Pencerede hiçbirşey gçremediniz mi? Telaşlanmayın, KDbg'ye birşey olmadı. Problemin kaynağı, bizim çalışabilir dosyaya herhangi bir hata ayıklama bilgisi koymamış olmamız, bu yüzden KDbg içeride neler olduğunu söyleyemez. -g anahtarı gerekli bilgiyi nesne dosyalarına koyar. Nesne dosyalarınızı (.o uzantılı) bu anahtar ile derlemelisiniz, şimdi komut:
gcc -g -c shuffle.c game.c
gcc -g -o game game.o shuffle.o
oldu. Bu, çalışabilir dosyanıza gdb ve KDbg neler olduğunu anlamasını sağlayan çengeller koyar. Hata ayıklama önemli bir yetenektir, bir tanesini iyi bilmeye zaman ayırmanıza değer. Hata ayıklayıcıların programcılara yardım edebilmesini sağlayan şey kaynak kodda 'Breakpoint' koyabilmesidir. Şimdi, shuffle fonksiyonunu çağıran satıra bir tane eklemeyi deneyin. Küçük bir kırmızı halkanın satırın yanında belirmesi lazım. Şimdi, F5'e bastığınızda program o satırda duracaktır. F8 basarak shuffle fonksiyonunun içerisine bir adım atın. Hey, şimdi shuffle.c dosyasının koduna bakıyoruz! Programın çalışmasını adım adım takip ederek neler olduğunu görebiliriz. Farenin işaretçisini bir yerel değişkenin üstüne getirdiğinizde hangi değere sahip olduğunu görebilirsiniz. Şirin. printf satırlarından daha iyi değil mi?

 

Özet

Bu makale, sizlere, C programlarının derlenmesi ve hatalarının ayıklanması konusunda kısa bir tur sundu. Derleyicinin geçtiği adımları ve bu adımların gerçekleşmesi için gcc'ye verilen anahtarları inceledik. Paylaşılan kütüphanelerin bağlanmasına bakıp, hata ayıklayıcılara bir girişle bitirdik. Ne yaptığınızı oğrenmek için daha çok çalışmaya gereksinim var, ama ümit ederim bu makale doğru adımla başlanmasına hizmet eder. Daha geniş bilgiyi, gcc, as ve ld nin man ve info sayfalarından bulabilirsiniz.

En çok şeyi, kod yazarken oğrenirsiniz. Pratik için, bu makaledeki kart programını kullanarak bir blackjack oyunu yazabilirsiniz. Hata ayıklayıcı kullanımı oğrenmek için biraz zaman ayırın. KDbg gibi bir GUI ile başlamak daha kolaydır. Her seferinde biraz fonksiyonalite eklerseniz, oğrenme işini halletmiş olursunuz. Hatırlatma, programınızı daima çalışır halde tutun.

Tam bir oyun yazmak için gereksiniminiz olan şeylerden bazılarının listesi.

 

Referanslar

 

Bu yazı için görüş bildiriminde bulunabilirsiniz

Her yazı kendi görüş bildirim sayfasına sahiptir. Bu sayfaya yorumlarınızı yazabilir ve diğer okuyucuların yorumlarına bakabilirsiniz.
 talkback page 

Görselyöre sayfalarının bakımı, LinuxFocus Editörleri tarafından yapılmaktadır
© Lorne Bailey, FDL
LinuxFocus.org

Burayı klikleyerek hataları rapor edebilir ya da yorumlarınızı LinuxFocus'a gönderebilirsiniz
Çeviri bilgisi:
en --> -- : Lorne Bailey <sherm_pbody(at)yahoo.com>
en --> tr: Bülent ÖZDEMİR <bulentozdem(at)hotmail.com>

2002-03-04, generated by lfparser version 2.27