[Makale] C’nin Standart Dosya Fonksiyonlarının Uyguladığı Tamponlama Mekanizması
Standart C fonksiyonlarını kullanmadan bir dosyanın her byte’ı üzerinde sırasıyla işlem yapmak isteyelim. Herhalde ilk akla gelecek yöntem doğrudan işletim sisteminin sistem fonksiyonlarını çağırmak olacaktır. Örneğin UNIX/Linux sistemlerinde dosyayı read fonksiyonuyla (Windows sistemlerinde ReadFile fonksiyonuyla) byte byte aşağıdaki gibi okuyabiliriz:
int fd;
ssize_t result;
unsigned char ch;
if ((fd = open("test", O_RDONLY)) < 0) {
perror("open");
exit(EXIT_FAILURE);
}
while ((result = read(fd, &ch, 1)) > 0) {
/* Okunan byte işleniyor */
}
if (result < 0) {
perror("read");
exit(EXIT_FAILURE);
}
close(fd);
[Makale] Programların Komut Satırı Argümanları
İşletim sistemi tarafından prosese geçirilen komut satırı argümanları program içerisinden çeşitli biçimlerde elde edilebilmektedir. En yaygın yöntem komut satırı argümanlarının programın başlangıç fonksiyonunun parametrelerinden elde edilmesidir. Örneğin, C ve C++’ta komut satırı argümanları main fonksiyonuna parametre olarak geçirilirler. Bu dillerin standartlarına göre programın başlangıç noktasını (entry point) belirten main fonksiyonunun parametrik yapısı ve geri dönüş değeri aşağıdaki iki durumdan biri biçiminde olmalıdır:
int main(void) { /* ... */ }
int main(int argc, char *argv[]) { /* ... */ }
[Makale] UNIX/Linux ve Windows Sistemlerinde Stdin, Stdout ve Stderr Dosyaları
Yalnızca UNIX/Linux sistemlerinde değil modern işletim sistemlerinin çoğunda aygıtlar birer dosyaymış gibi ele alınmaktadır. Örneğin klavye ve ekran -aslında birer dosya olmadığı halde- işletim sistemi tarafından sanki birer dosyaymış gibi işleme sokulurlar. Aygıtlara ilişkin bu tür dosyalar için de birer dosya betimleyicisi ve dosya nesnesi vardır. Bu betimleyicilerle işlem yapıldığında işletim sisteminin dosya alt sistemi aslında bu dosyaların birer aygıta ilişkin olduğunu anlar ve okuma/yazma amacıyla o aygıtlara yönelir. UNIX türevi sistemlerdeki çokbiçimliliği (polymophism) andıran bu tasarıma Sanal Dosya Sistemi (Virtual File System) denilmektedir.
[Blog] C#'taki Yapı ve Sınıf Nesneleri Nerede Yaratılıyor?
Pek çok C# programcısının sınıf ve yapı kavramlarıyla stack ve heap kavramlarını yanlış bir biçimde ilişkilendirdiğini görüyorum. Örneğin, “yapı nesneleri stack’te sınıf nesneleri heap’te tutulur” biçiminde yanlış anlaşılmaya yol açacak bilgiler veren yerli ve yabancı çok sayıda yazı ve makaleyle karşılaştım. Konuya biraz açıklık getirmek istiyorum.
[Makale] UNIX/Linux Sistemlerinde Dosya Betimleyicilerinin Anlamı
UNIX/Linux sistemlerinde her prosesin proses tablosu yoluyla erişilen bir dosya betimleyici tablosu (file descriptor table) vardır. Dosya betimleyici tablosu bir gösterici dizisi biçimindedir. Betimleyci tablo içersindeki her gösterici açılmış bir dosyanın bilgilerinin tutulduğu ve ismine dosya nesnesi (file object) denilen bir veri yapısını gösterir. open fonksiyonundan elde edilen dosya betimleyicisi (file descriptor) prosesin dosya betimleyici tablosunda bir indeks belirtmektedir.
[Makale] Proseslerin Çevre Değişkenleri
Modern işletim sistemlerinde her prosesin bir çevre değişken bloğu vardır. Prosesin çevre değişken bloğu çevre değişkenlerinden ve onların değerlerinden oluşmaktadır. Örneğin, MESAJ bir çevre değişkeninin ismi olabilir, “Merhaba Dunya” ise onun değeri olabilir. Çevre değişkenleri pek çok işletim sisteminde proses yaratılırken belirlenebilmekte ya da üst prosesten (parent process) aktarılabilmektedir. Çevre değişkenlerinin üst prosesten aktarılması en çok karşılaşılan tipik durumdur.
Pek çok C# programcısının sınıf ve yapı kavramlarıyla stack ve heap kavramlarını yanlış bir biçimde ilişkilendirdiğini görüyorum. Örneğin, “yapı nesneleri stack’te sınıf nesneleri heap’te tutulur” biçiminde yanlış anlaşılmaya yol açacak bilgiler veren yerli ve yabancı çok sayıda yazı ve makaleyle karşılaştım. Konuya biraz açıklık getirmek istiyorum.
Bir nesne ya da olgunun bütün içerisindeki yerinin belirlenmesi ve diğer nesne ya da olgularla ilişkilerinin betimlenmesi sürecine kategorizasyon deniyor. Kategorizasyonun sentezlemeyi artırarak öğrenmeye katkıda bulunduğunu söyleyebiliriz. Sentezleme süreci de anlamsal bellek (semantic memory) içeriğindeki ilişkileri sağlamlaştırıyor olabilir.
Bana çok sorulan sorulardan biri de budur. Diyorlar ki, “yahu neden global değişkenlere varsayılan bir değer (tipik olarak sıfır değeri) atanıyor da yerel değişkenlere atanmıyor? Bu hak mı, adalet mi?”. Özellikle C# ve Java gibi içerisine değer atanmamış değişkenlerin kullanılmasına izin verilmeyen dillerde çalışanlar yerel değişkenlerin otomatik olarak sıfırlanmamasından memnun değiller.
Nedense dizinlerin erişim hakları pek çok UNIX/Linux programcısına karmaşık geliyor. Geçen haftalarda bu konuyla ilgili birkaç soruyla karşılaştım. Genel bir açıklama yaparsam tereddütleri olanların kafalarındaki soru işaretlerini belki biraz azaltabilirim diye düşünüyorum.
C#’taki foreach döngülerinde kullanılan döngü değişkenlerinin neden read-only olduğu bana çok soruluyor. Soranların çoğu döngü değişkeninin read-write olması durumunda işlevselliğin artacağını iddia ediyorlar. Örneğin onlara göre eğer foreach döngü değişkeni read-only olmasaydı bir dizinin tüm elemanları belirli bir değerle şöyle doldurulabilirdi:
foreach (int x in a)
x = val;
i, j, k, l, m, n döngü değişkeni olarak en çok tercih edilen isimler. Nerede bir for döngüsü görseniz döngü değişkeninin ismi büyük olasılıkla ya i’dir ya j’dir ya da k’dır. Hiç düşündünüz mü neden a değil, b değil, c değil de i, j, k? Eski kuşak programcılar (yani bayağı bir eskiler) bunun nedenini iyi bilirler. Dolayısıyla bu açıklamayı onlar için değil yeni kuşak yazılımcılar için yapacağım.
restrict gösterici kavramı resmi olarak C99 ile standartlara girdi. C++ standardizasyon komitesi restrict göstericilere hiç sıcak bakmadı. C++0x’te de restrict göstericilerin eklenmesine yönelik bir işaret yok.
Yıllar önceydi. Bir arkadaşımın kodunu inceliyordum. Kodunda max ismini verdiği bir değişken vardı. Ben de doğal olarak bu değişkenin en büyük değeri tuttuğunu sanarak kodu anlamlandırmaya çalıştım. Ancak birşeyler tam yerine oturmuyordu bir türlü. Meğerse arkadaşım başlangıçta en büyük değeri bulup kullanırken sonra en küçük değerin bulunup kullanılması gerektiğini düşünerek algoritmasını değiştirmiş. Fakat değişkenin ismini değiştirmemiş. Düşünebiliyor musunuz, bir değişken var, ismi max fakat bu aslında en küçük değeri tutuyor :-).
Pek çok C# programcısının using namespace direktifinin kullanımını pek iyi bilmediğini görüyorum. Bu konuda bazı açıklamalar yapayım dedim. Ancak bu yazıda extern alias ve using alias direktifleri üzerinde durmayacağım. Bunlara ilişkin de bir makale yazmayı planlıyorum.
Öncelikle using namespace direktifinin, isim alanlarının ilk elemanları olacak biçimde -eğer varsa- extern alias direktiflerinden sonra belirtilmek zorunda olduğunu anımsatayım. Örneğin aşağıdaki bildirim geçersizdir:
Gecenin bir yarısı üzerinde çalıştığınız programı tam da derleyip çalıştıracakken telefonunuz çalsa ne yapardınız? Derlemenin sonucunu mu beklerdiniz, yoksa bir donanım kesmesi oluşmuşçasına telefona mı koşardınız? İşte geçen gün ben derlemenin sonucunu beklemeyi tercih ettiğim için çalan telefona yetişemedim. Fakat sonra içimi bir endişe kapladı. Yoksa gecenin bu saatinde kötü bir haberin iletilmesi için mi aranmıştım?..
Bu problemde programcı içi dolu bir şekli taşımak istemektedir. Taşıma sırasında eski şekli silerek yenisini çizer. Fakat şekli taşırken titreme (flickering) oluşmaktadır... Bu biçimdeki titreme probleminin nedeni silinecek bölgenin büyük bir kısmının önce zemin rengiyle hemen ardından da asıl renkle boyanmasıdır. (Problem genel olmasına karşın ben burada kod örneklerini C/C++ ile API düzeyinde değil C# ile .NET üzerinde vereceğim. Hani benim de C# kullanmaya gönlüm razı olmuyor. Fakat belki böylece daha fazla kişiye hitap edebilirim :-) )
POD (Plain Old Data) C++’ta ayrıntıları pek bilinmeyen karışık bir konudur. Pek çok dökümanda POD terimi geçtiği halde pek az C++ programcısının bu konuyu tam olarak bildiğini görüyorum.
C’de genel olarak (ister int, long gibi aritmetik türden olsun, isterse dizi ya da yapı türünden olsun) her türden nesnenin byte’ları bellekte ardışıl bir biçimde tutulmaktadır. Nesnenin adresini alarak o adresten itibaren sizeof değeri kadar byte’ı memcpy gibi bir fonksiyonla bir alana aktarıp aynı biçimde geri alırsak eski nesneyi elde ederiz. C standartları bunu garanti etmektedir. Örneğin:
Doğal dillerdeki kurallar matematiksel anlamda kesin değildir, bunların istisnaları çoktur. Zaten bizi herhangi bir şey öğrenirken bezdiren şeylerden biridir istisnalar. Örneğin İngilizce’de daha yüksek demek için high sıfatı higher haline, tall sıfatı taller haline geliyor. Pek çok sıfatın bu biçimde "er" eki aldığını görüyoruz. Fakat bu kesin bir kural mı? Hayır, bunun pek çok istisnası var. Örneğin, (her nedense) kötü bad olduğu halde daha kötü için worse kullanılıyor. (Ana dil gibisi var mı? Hepimiz kırmızı için kıpkırmızı, mavi için masmavi denildiğini biliriz. Bunun bir kuralının olup olmadığını sorgulamamışızdır. Zaten umurumuzda da değildir. Kimse kırmızı için maskırmızı demez.)
Aşağıdaki ifadeyi inceleyiniz:
b = ++a + foo() * bar();
pek çok C ve C++ programcısı işlemlerin aşağıdaki sırada yapılacağının garanti olduğunu sanıyor:
Son yıllarda özellikle Internet siteleri yoluyla köşe dönme öyküleri bizim gibi psikoekonomik (!) yapısı bozuk olan toplumlarda sanrısal patlamalara yol açtı galiba. Gün geçmiyor ki (lafın gelişi tabi) parlak fikir sahibi bir girişimci kapımızı çalmasın. Ampulü yananın fellik fellik ortak aradığı bir dönemden geçiyoruz.
CSD C ve Sistem Programcıları Derneği