Evet, size_t'de bir yanlış anlama var.
Alıntı (Salih Dinçer):
> size_t: "Derleyicinin, sistem kaynaklarına ve kodun işleyişine göre karar verdiği (tanımladığı) bir veri türüdür."
Sistem kaynakları deyince bellek miktarı gibi bir kavram anlaşılabiliyor. size_t'nin sistemle öyle bir ilgisi yoktur.
Kodun işleyişi derken de çalışma zamanı ile ilgili bir kavramdan bahsediyorsan o da olamaz. Hatırlarsak, D derlemeli bir dil olduğundan bütün türler derleme sonunda bellidir. (Aslında kod işlemeye başladığında tür diye bir kavram kalmamıştır; ama konuyu dağıtıyorum.)
size_t'nin ne olduğunu sana bir soru sorarak anlatayım. Dizi gibi işleyen bir yapı tasarlıyorsun:
struct Dizi
{
/* ... */
BuTürNedir uzunluk()
{
/* ... */
}
}
uzunluk() işlevinin dönüş türü nedir? Nasıl karar verirsin? Öyle bir tür olmalı ki bu sistemdeki bütün adet kavramlarını temsil edebilmeli.
Veya derleyici tarafından yanıtlayalım: Derleyici şu döngüde i'nin türünü ne seçmelidir?
foreach (i; 0 .. 10) {
/* ... */
}
10 yerine int.max yazsam? uint.max yazsam? Bunlara göre farklı tür mü seçmelidir? Ama öyle yapsa döngü içinde benim kodlarım farklı işleyebilir. Programcı olarak orada bir garanti beklerim.
Sonuçta şöyle karar verilmiş: programın derlendiği sisteme uygun bir tür seçelim ve onun adına size_t diyelim. Program adet ve indeks gibi kavramları temsil ederken hep size_t yazarsa hiçbir yerde sorun kalmaz.
Ayrıca bu türün işaretsiz bir tür olduğunu da garantileyelim. Yoksa programcı örneğin 64 bitlik bir sistemde belleğin uzunluğunu bile söyleyemez. 64 bitlik sistemde 2 üzeri 64 adet adres bulunabildiğine göre size_t'yi long seçsek yalnızca yarısını söyleyebiliriz.
size_t, programın derlendiği anda belirlidir ve uygun bir işaretsiz bir türdür. Her ne kadar "uygun bir türdür" diye fazla serbest konuşuyorsam da 32 bitlik sistemlerde uint'in, 64 bitlik sistemlerde de ulong'un eşdeğeri olduğu neredeyse kesindir.
Neyin takma ismi olduğunu /usr/include/d/dmd/druntime/import/object.di dosyasında görüyorum:
alias typeof(int.sizeof) size_t;
Demek ki aslında karar daha derinlerden geliyor: derleyici o sistemde .sizeof'un türü olarak neyi seçmiş ise ona size_t diyoruz.
Programcının yapması gereken son derece basit: size_t, adet veya indeks gibi kavramların türüdür. .length, .sizeof, öğrenciAdedi(), çekirdekAdedi(), vs. hep size_t türünde seçilmelidir. O tarafı bu kadar basit.
Öte yandan size_t, bu programdaki veri için hiç uygun değildir. Çünkü verimiz adet değil, indeks değil. Bitleri ile ilgilendiğimiz değişkenlerle ilgileniyoruz.
Yan not: size_t'nin işaretsiz bir tür olmasının getirdiği bir sakınca vardır. Eksi değer taşıyamadığından çıkarma işleminde kullanıldığında yanlış sonuç verebilir: i-j dendiğinde j daha büyük değer ise i ile j arasındaki uzaklığı elde etmemiş oluruz. Çıkarma işleminde kullanmaya uygun tür sizediff_t'dir çünkü o işaretli bir türdür.
Alıntı:
> Kullanma sebebim ise; aranan veri ile verinin kendisinin değişebilirliliği. Yani veriler[] dizi en küçük ubyte olabilir ama dilediğimizde sınıf veya işlevde değişikliğe gitmeden diğer veri türleri de kullanılmalıdır.
"İşlevde değişikliğe gitmeden" derken derleyicinin değişiklik yapmamasından bahsediyorsan bu olanaksızdır. Sonuçta işleyen makine kodu tam da o türe uygun yazmaçlarla vs. derlenmiştir. Türe karar verilmiştir.
Eğer programcının değişiklik yapmamasından bahsediyorsan o zaman ya işlev yükleme (overloading) olanağından yararlanarak her tür için farklı işlev yazmak zorundasın, ya da bunun güçlüğünü farkettiğinde şablon yazmalısın.
Başka bir seçeneğin de verinin türünü örneğin ulong seçmek ve olayı bitirmektir. D'nin tamsayı türlerinin bit sayılarının belirli olması yararlı. ulong'un her zaman 64 bit olduğunu biliyoruz. Ek olarak şunlar da var:
http://dlang.org/phobos/std_stdint.html
O türler sanıyorum C'ye sonradan eklenen tür isimlerinin aynıları.
Alıntı:
> İşte bu yüzden yan anlam olarak da şunu zannediyorum:
"...işlevde kullanılırken bağlandığı veri türü ubyte ise temsil eden size_t de ubyte olur" mu?
Bu noktaya kadar anlaşılmıştır ama, hayır ubyte olmaz. Kullanılırken bağlandığı tür istiyorsan şablon yazman gerek. D şablon kolaylığı konusunda inanılmayacak kadar kolay.
Alıntı:
> Ayrıca aranılan veri türü üç bit de olabileceği gibi 64 bit de olabilir. O yüzden size_t kullanmıştım.
İşte o zaman ya ulong kullanmalısın veya yukarıdaki sayfadaki ismi daha açık olan uint64_t'yi. Belki de en büyük tamsayı türü anlamına gelen uintmax_t'yi. Onun tanımı da şurada:
/usr/include/d/dmd/druntime/import/core/stdc/stdint.di
Görüldüğü gibi o da zaten ulong:
alias ulong uintmax_t;
Eğer ucent sonunda gerçekleştirilirse belki de ucent olur. (?)
Alıntı:
> Yine yukarıdaki gibi aynı mantıkla, xKelime değişkenini ulong olarak tanımlarsam derleyici de size_t'li işlevleri de ulong yapıyor olabilir mi?
Senin ilacın şablon! ;)
Alıntı:
> Eğer yukarıda şüphelendiğim gibi yanılgı içindeysem size_t'leri tam anlamıyla öğrenene kadar kullanmamalıyım...:)
Bence adet ve indeks kavramları için size_t kullan. Çıkarma işleminde sonucun çok büyük çıkabileceğini de aklında tut.
Şimdi sana bit işlemleri ile ilgilenen küçük bir şablon yazayım. İşine yarayıp yaramayacağına veya zor olup olmadığına sen karar ver:
import std.stdio;
/**
* Verinin en üst ve en alt dört biti dışındaki bitlerinin
* sıfırlanmışını döndürür.
*/
T ortasıSıfırlı(T)(T veri)
{
/* Not: Aşağıdaki üç değişkenin başına enum yazmak daha
* uygun olur. Örneği bulandırmamak için öyle yazmadım. */
size_t toplamBit = veri.sizeof * 8;
T altBitlerMaskesi = 0b1111;
T üstBitlerMaskesi = cast(T)(altBitlerMaskesi << (toplamBit - 4));
/* Yukarıdaki cast'e neden gerek olduğunu yazı içinde
* anlatıyorum. */
return veri & (altBitlerMaskesi | üstBitlerMaskesi);
}
/*
* 101010... deseninde belirtilen türde değer döndürür.
*/
T birSıfır(T)()
{
return cast(T)(0xaaaa_aaaa_aaaa_aaaa);
}
void main()
{
ubyte b = birSıfır!ubyte();
ushort s = birSıfır!ushort();
uint i = birSıfır!uint();
ulong l = birSıfır!ulong();
writefln("%b => %b", b, ortasıSıfırlı(b));
writefln("%b => %b", s, ortasıSıfırlı(s));
writefln("%b => %b", i, ortasıSıfırlı(i));
writefln("%b => %b", l, ortasıSıfırlı(l));
}
Çıktısı:
'10101010 => 10101010
1010101010101010 => 1010000000001010
10101010101010101010101010101010 => 10100000000000000000000000001010
1010101010101010101010101010101010101010101010101010101010101010 => 1010000000000000000000000000000000000000000000000000000000001010
'
Şablonun karışıklıkları şunlar:
-
İşlev şablonunun iki parantezi oluyor. Birinci parantez, T'nin bir tür olduğunu ve programdaki kullanıma göre derleyici tarafından seçileceğini bildiriyor. (Çalışma zamanından çok önce.)
-
İşlevin geri kalanında o tür ne ise onun yerine T yazıyoruz.
ortasıSıfırlı() işlevinde cast kullanmak zorunda kaldım. Bu, "integer promotions" (bazen "integral promotions" da diyorlar) yüzündendi. Şurada "Integer promotions" başlığında:
http://dlang.org/type.html
C'ye dayanan kurallara göre << gibi işlemler ya int ya da uint türü ile yapılırlar. Yani T ubyte bile olsa << işleminin kendi türü int oluyor. int'i de üstBitlerMaskesi değişkenine cast'siz atayamıyoruz.
Çok önemli bir hatırlatma daha: şablonların sihiri de derleme zamanındadır. Derleyici, programda kullandık diye tam dört tane ortasıSıfırlı() işlevi oluşturur. Bunların makine kodları kullandığımız dört türe göredir.
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]