C dilinde dinamik davranış
Esenlikler tekrardan, aslında farklı bir fikir hakkında yazı yazacaktım fakat artık daha sonraya kaldı o fikir, C [1] ile ilgili yazdığım ilk yazı bu. İlk C yazdığım zamanlarda her insan gibi ben de hata yapıyordum (memory leak, double-check free() [2] gibi gibi) neticede doğal olarak zamanla insan ögreniyor. Konumuza gelirsek..
C dili diğer dillerin sunduğu hazır soyutlama katmanlarından ve derleyici yani compiler [3] seviyesineki sanal tablolardan bir tık uzaktır. Dilin sade yapısı, bir programcıya donanım üzerinde tam kontrol verirken, ve de aynı kod tabanında farklı davranışları yönetme ihtiyacı olduğunda kafaları karıştırabilir.
C dünyasında her şey açıktır, sadedir. Derleyici sizin yerinize arkada sihirli* işler yapmaz, ne yazdıysanız makine koduna birebir çevirilir (object file [4] ise değişir bu). Eğer çalışma zamanında yani runtime'da hangi fonksiyonun çalışacağına karar vermek istiyorsanız, bu mimariyi kendi ellerinizle kurmak zorundasınız*. Bu durum ilk başta bir dezavantaj gibi görünse de aslında programlamayı anlamanın tek* yoludur.
Bir projeyi büyütürken karşılaşılan en büyük sorunlardan biri, sabit bağımlılıkların yarattığı katı yapıdır. Kodunuzun içi 'if' ve 'else' [5] bloklarıyla dolup taşmaya başladığında, yeni bir özellik eklemek veya mevcut bir davranışı değiştirmek adeta bir kabusu* dönüşür. Statik çağrılar, derleme aşamasında her şeyi kilitlediği için esneklik tamamen kaybolur. İşte bu noktada, C'nin keskin bıçağı olan fonksiyon işaretçileri yani function pointers sahneye çıkar.
C'de fonksiyonlar da bellekte belirli bir adreste tutulur [6]. Dolayısıyla bir değişkenin adresini işaretçilerle tutabildiğimiz gibi, bir fonksıyonun adresini de bir değişkene atayabilir ve gerektiğinde o adres üzerinden çağrı yapabiliriz. Bu yaklaşım, program akışını statik zincirlerden kurtararak dinamik bir yapıya kavuşturmanın temel yoludur.
Fonksiyon işaretçilerinin temelleri
Fonksiyon işaretçişi [8] tanımlamak ilk bakışta karmaşık ve zor bir sözdizimi gibi gelebilir, ancak mantığı son derece basittir. Hedef fonksiyonun imazsını birebir taklit eden bir işaretçi değişkeni oluşturursunuz..
#include <stdio.h>Bu ham mekanizma, davranışın çalışma anında değiştirilebilmesinin temel taşıdır. Neticede gerçek projelerde işler sadece tek bir fonksiyondan ibaret değildir. Sistemler büyüdükçe birbiriyle ilişkisi birden fazla davranışı aynı çatı altında toplamak ve yönetmek lazımdır.
void text(void) { printf("Text.\n"); } int main(void) { void (*function_pointer)(void) = text; function_pointer(); return 0; // NULL. }
Davranış tabloları ile mimari kurmak
Son kısımlara geliyoruz, diyelim ki bir veri işleme hattı kuruyorsunuz ve bu hattın çıktısı farklı ortamlara yönlendirilebiliyor. Hangi ortama yazılacağını derleme zamanında değil, tamamen çalışma anındaki ayarlara göre belirlemek istiyorsunuz. Ana program, alt seviye detayları bilmek zorunda olmamalıdır, sadece ortak bir zincir üzerinden işini yürütmelidir.
#include <stdio.h>Bu tarz bir tasarım da veyahut büyük kod tabanlarında modülerliği korumanın ve bağımlılıkları tersine çevirmenin en saf yoludur. Arka planda gizlenmiş bir bellek tahsisi olmadığı için, sistemin her bit byte'ın ve her bir işlemci çevrimini tam olarak kontrol altında tutarsınız.
struct handler; struct operations { void (*execute)(struct handler *self); }; struct handler { const struct operations *opers; }; void concrete_execute(struct handler *self) { printf("Text.\n"); } const struct operations concrete_opers = { .execute = concrete_execute }; int main(void) { struct handler h = { .opers = &concrete_opers }; if(h.opers && h.opers->execute) { h.opers->execute(&h); } return 0; // NULL. }
Gün sonunda, C programcıya her zaman sınırları olmayan bir alan* sunar. Fonksiyon işaretçileri ve bu tarz davranış tabloları, dili iyi bir şekilde esnek tutmanın ve mühendislik disiplinini iyi bir seviyede tutmanın temiz yoludur.
Umarım bu yazı size yardımcı olmuştur.
Kaynaklar:
[1] - https://en.wikipedia.org/wiki/C_(programming_language)
[2] - https://en.wikipedia.org/wiki/C_dynamic_memory_allocation
[3] - https://en.wikipedia.org/wiki/Compiler
[4] - https://en.wikipedia.org/wiki/Object_file
[5] - https://en.wikipedia.org/wiki/Conditional_(computer_programming)
[6] - https://en.wikipedia.org/wiki/Memory_address
[7] - https://downdoot.com/r/programmingmemes/comments/1dfn3jr/exactly/
[8] - https://en.wikipedia.org/wiki/Function_pointer
[9] - https://downdoot.com/r/programmingmemes/comments/tmj4lr/imagine_using_x_x_1/