C Dilinde Kodlar Nasıl Derlenir?¶
Önceki konularda C dilinde program oluşturma aşamalarını ve hafıza yönetimini incelemiştik. Yazdığımız kodların; program oluşturma aşamasında önce Ön İşlemci’den geçtiğini ve Derleyici tarafından işlemci kodlarının oluşturulup Bağlayıcı ile programımızın çalışmaya hazır hale getirildiğini biliyoruz. Bu bölümde derleyici tarafından kodlarımızın nasıl derlendiğini, bu işlem sırasında nelere dikkat etmemiz gerektiğini inceleyeceğiz.
C dilinde yazılan her program main() fonksiyonuna ihtiyac duyar.
main() fonksiyonu çağırılmadan önce yapılması gereken işlemler genellikle
reset vektörüne yazılır ve işlemler bittikten sonra program main()
fonksiyonuna koşulsuz dallanır. Eğer kodunuzda bir main() fonksiyonu
tanımlamazsanız Bağlayıcı hata verecektir.
Dosya Tipleri¶
C dilinde kullanılan dosya tiplerini aşağıda açıklamaya çalıştım.
Kaynak Dosyası¶
Kaynak dosyası(İng:source file) kodlarımızın bulunduğu metin tabanlı bir dosyadır. Herhangi bir metin editörü veya geliştirme ortamı ile bu dosyaları hazırlayabiliriz. C dilinde kaynak dosyalarının uzantısı “.c”’dir.
Başlık Dosyası¶
Başlık dosyası(İng:header file) kaynak dosyasında bulunan fonksiyon ve değişkenlerin diğer kaynak dosyaları tarafından kullanılabilmesini sağlar. C dilinde başlık dosyalarının uzantısı “.h”’dir. Başlık dosyaları içinde fonksiyon prototipleri, kullanılacak tanımlar ve bazı durumlarda satıriçi(İng:inline) fonksiyonlar bulunur. Özellikle kod taşınabilirliği ve modülerlik için başlık dosyalarının düzgün hazırlanması önemlidir.
Birleştirici Dili Dosyası¶
Birleştirici(İng:Assembler) dili dosyası özellikle C derleyici ortamlarında başlangıç(İng:Startup) dosyası olarak kullanılır. C ve ASM kodları ayrı ayrı derlenip linker aracılığı ile program oluşturulur. ASM dosyası mikrodenetleyiciye ait açılış vektör yerleşimi, reset vektörü ve özel kodlar içerebilir.
Örnek Proje¶
C dilinde her assembler dosyası ve kaynak dosyası ayrı ayrı derlenerek obje dosyaları oluşturulur ve bu oluşturulan obje dosyaları linker tarafından birleştirilir. Derleme aşamasında kaynak dosyalarının içerdiği başlık dosyalarının belirli bir sıra ve düzende eklenmiş olması derleme hatalarını, modüler yapıların korunmasını, eğer katmanlı uygulama yazıyorsanız katmanlar arası ayrımı ve derlenme süresini optimize edecektir.
Aşağıdaki şekilde 3 katmanlı bir projeye ait uygulama yapısını görüyoruz. main modülü özellikle bare-metal programlarda süper döngü(İng.Super loop) olarak kullanılır.
Örnek uygulama katmanı
Yukarıda örneğini verdiğim katmanlı uygulamalarda genelde üst katmanların bir alt katmandan daha aşağı ve alt katmanların üst katmanlara erişmesi istenmez. Bunun için içiçe eklenmiş başlık(İng.Nested-Header files) dosyalarının kullanılmaması gereklidir. Aşağıda her bir modül için örnek kodları bulabilirsiniz.
Main Modülü¶
Main modülüne ait bir başlık dosyası olmadan da kodlarımız çalışır. Bu
örnek kodda main modülüne ait bir başlık dosyası kullanmayacağız.
uygulama_kontrol değişkenine kesme fonksiyonu içinde değer ataması
yapılmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include <MIKRODENETLEYICI_MODEL.h>
#include <stdbool.h>
#include "uygulama.h"
volatile unsigned int uygulama_kontrol;
void main(void)
{
//....
//....
Uygulama_Init();
//....
//....
while (1)
{
//....
//....
if (FALSE != uygulama_kontrol)
{
uygulama_kontrol = FALSE;
Uygulama_Kontrol();
//....
//....
}
//....
//....
}
}
void Timer_ISR(void)
{
uygulama_kontrol = TRUE;
//....
}
|
Uygulama Modülü¶
- Kaynak Dosyası
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <stdio.h>
#include "paket.h"
#include "uygulama.h"
static unsigned char paket[PAKET_BOYUT_MAX];
void Uygulama_Init(void)
{
//....
//....
Paket_Init();
//....
//....
}
void Uygulama_Kontrol(void)
{
unsigned int uzunluk;
//....
//....
uzunluk = snprintf(paket, PAKET_BOYUT_MAX, "Deneme metin\n");
Paket_Gonder(paket, uzunluk);
}
|
- Başlık Dosyası
1 2 3 4 5 6 7 8 9 10 11 | #ifndef UYGULAMA_H
#define UYGULAMA_H
//....
//....
void Uygulama_Init(void);
void Uygulama_Kontrol(void);
//....
//....
#endif /* UYGULAMA_H */
|
Paket Modülü¶
- Kaynak Dosyası
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h>
#include "surucu.h"
#include "paket.h"
void Paket_Init(void)
{
//....
//....
Surucu_Init();
//....
//....
}
void Paket_Gonder(unsigned char* veri, unsigned int uzunluk)
{
//....
//....
Surucu_Gonder(veri, uzunluk);
}
|
- Başlık Dosyası
1 2 3 4 5 6 7 8 9 10 11 12 13 | #ifndef PAKET_H
#define PAKET_H
//....
//....
#define PAKET_BOYUT_MAX (20)
void Paket_Init(void);
void Paket_Gonder(unsigned char*, unsigned int);
//....
//....
#endif /* PAKET_H */
|
Sürücü Modülü¶
- Kaynak Dosyası
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <MIKRODENETLEYICI_MODEL.h>
#include <stdio.h>
#include "surucu.h"
void Surucu_Init(void)
{
//Haberleşmede kullanılacak olan mikrodenetleyici donanımına ait register
//ayarları burada yapılır.
//....
//....
}
void Surucu_Gonder(unsigned char* veri, unsigned int uzunluk)
{
//Haberleşmede kullanılan donanımın gönderme için kullanılacak register
//atamaları burada yapılır
//....
//....
}
|
- Başlık Dosyası
1 2 3 4 5 6 7 8 9 10 11 | #ifndef SURUCU_H
#define SURUCU_H
//....
//....
void Surucu_Init(void);
void Surucu_Gonder(unsigned char*, unsigned int);
//....
//....
#endif /* SURUCU_H */
|
Derleme Açıklamaları¶
Yukarıdaki örnek proje derlenirken hangi modülün önce derlendiğinin bir önemi yoktur ve tüm modüller kendi başlarına derlenebilir modüllerdir. Bu sayede paketleme katmanına ait kaynak ve başlık dosyası başka bir projeye taşınabilir ve paketleme katmanı sadece bir sürücü katmanına ihtiyaç duymaktadır. Yeni projede sürücü katmanı yukarıdaki standardda yazılmış ise paketleme katmanınız sorunsuz çalışacaktır.
Not
Yukarıdaki örnekte başlık dosyaları hiçbir #include direktifine sahip
değildir. Çok gerekmedikçe ben başlık dosyaları içinde özellikle projeye ait
diğer modüllerin başlık dosyalarının içerilmesini önermiyorum. Eğer
uygulama.h dosyası içinde paket.h içerilse idi main modülünden
paket modülü fonksiyonlarına erişebilirdiniz.
Bu tarz kontrolsüz erişimler mantık hataları yapılmasına yardımcı olur.
Main modülü içinde Uygulama_Init() fonksiyonu çağırılmadan
Paket_Gonder() fonksiyonu çağırılır ise istenmeyen sonuçlar ortaya
çıkacaktır.