Bu makale veri tiplerinin kısa açıklamalarından ibarettir. Temel veri tipleri tablosunu Temel Veri Tipleri 1 başlıklı yazıda bulabilirsiniz.

Adı üstende! Temel(primitive) 🧩 veri tipleri aslında programlamanın değil ama, orta seviye bir dil için onu öğrenmenin temeli🎯 sayılabilir. Haliyle teorik binlerce satırlık bilgiyi herhangi bir yerden zaten bulabilirsiniz. Ben teknik anlamından ziyade olası en basit şekliyle kafanızda canlandırmaya çalışacağım; yoksa teknik olarak çok daha ayrıntılı bir konudur. Haliyle bu yazı, kod yazmada acele edenler için değildir; evde denemeyiniz!🤪

Değişkenler için, daha çocuk yaşta cebirdeki denklem ve eşitsizliklere giriş ile beraber başımıza musallat olan X'e karşılık gelir diyebiliriz.😌 Yani aslında değişken kavramı yazılıma has değil; matematikle birlikte fizik, kimya vb. diğer sayısal bilimler için geçerli bir kavramdır. Örneğin fizikte yaptığınız deneyde ölçtüğünüz basınç, sıcaklık, hız, sürtünme gibi değerler birer değişkene karşılık gelebilir. Hani bağımlısı, bağımsızı olan; 2 bilinmeyenli denklemlerin meşhur bilinmeyeni, büyük üstat Ömer Hayyam ile öne çıkan X. Bilgisayarlarda ise ilk kullanıldığı zamanlarda kavram olarak da benzer bir kavram iken, günümüzde belleğin farklı bölgelere ayrılması ile veriye daha hızlı erişim ve belleğinin etkin kullanım amacı ile geliştirilmiştir.

Günlük Hayat Örnekleri

Şimdi bir hesap makinesinde  çeşitli işlemler yaptığımızı düşünelim. Elde ettiğimiz sayıyı başka işlemlerde kullanmak üzere hafızaya almak için M+ tuşuna basarız. En kaba tabiriyle değişken; hafızaya almak için bastığımız bu tuşun işlevini görür diyebiliriz. Sonraki işlem de bir sonuç doğuracak; sonrasında bir tane, bir tane daha... Aslında hesap makinesi adı üstünde gerçek bir 'compute' aracıdır; 2 ayrı işlemi üstüste yapıyorsanız sorun yok. Ancak şu an üstüste işlemler yapıp hafızada bekletmeye ihtiyacımız var ama 'hesapsayar'ımız çuvalladı! Başka bir sayıyı hafızaya alamıyor. Şimdi biz ona tekrar veri gireceğiz ve onun bize bir 'bilgi' daha üretmesi gerekiyor. Tabi bu bilgi insan algısındaki bilgi ile aynı değil düşünürken basitliği elden bırakmayın. Yaptığımız işlemlerle birlikte aynı zamanda rastgele erişimli hafıza(ram) dediğimiz hadiseye ihtiyacımız doğdu. Programlama dillerinde hesap makinesindeki gibi tek değişkenlik bir sınır olması söz konusu olamaz. Bizler için bunun bir sınırı yoktur. 'Yoktur'dan kastım günümüzdeki ram kapasiteleri ile değişkenlerin ramde kapladıkları boyutlar göz önüne alınırsa neredeyse yoktur. Bizim M+ tuşumuzdan birçok sayıda bulunur. Ayrıca hesap makinesinde tek bir değeri tuttuğumuz için buna isim vermeye gerek olmasa da biz değişkeni hafızaya çıkartırken bir isim verir ve o değişkene ulaşmamız gerektiğinde bu isim üzerinden ulaşırız. Değişken isminin amacı bir nevi hesap makinesindeki MR tuşuna benzer.

Elektriğin taşınması konusunu düşünelim. Muhteşem mucit Nikola Tesla, aslında yüksek gerilimi kablosuz bir şekilde şehirlere kadar taşıyarak herkesin ücretsiz kullanımına sunmak istiyordu. Bu çabaları malum çevrelerce engellense de kablolu haliyle dahi yüksek gerilimin dağıtımını ona borçluyuz. Şebeke elektriği, kuvvetli akım tesislerinden çıkıp önce alçak, orta ya da yüksek enerji hatlarıyla alternatif gerilim formunda evimize yakın trafolara taşınır. Oradan binalara 🏘 dağıtılarak 220V/50Hz civarında stabil tutulmaya çalışılır. Arada bu trafonun olmadığını düşünürseniz, evinizde 1000V gibi bir gerilim istemezdiniz sanırım. 🔥 Olsa olsa three-phase ihtiyacınız olabilir. Ama bir sanayi tesisiyseniz 🏭 muhtelemen yüksek gerilime ihtiyacınız olacaktır. Evimize kadar gelen alternatif akım, büyük abilerine nazaran evimizdeki küçük trafolarla -biz onları adaptör olarak biliyoruz- 9V, 5V gibi değerlere düşürülerek doğrultulur ve adı üstünde doğru akım halinde cihaza 📱 verilir. Televizyonlar gibi direk prize taktığınız cihazların içinde de elektrik dolaşmadan öncelikli olarak bu işlem gerçekleşir. Yine 🚗 araçlarımızda kullandığımız aküler; gerilimi 12V olmasına karşın 72Ah gibi bir akımla aracın ilk andaki enerji ihtiyacını karşılar. Yani değişkenler de benzer şekilde ihtiyacı karşılayacak şekilde kullanılmalıdır.

Mesela bir içecek alacağız. İçeceği içebilmek için doldurduğumuz bardak 🍷 bir nevi değişkene benzetilebilir. Ortada bir şekilde ihtiyacımız kadar olan içeceği alabilme problemi var. Bardağa koyup istediğimiz yere taşıma çözümünü getirmiş oluyoruz. İçtikten sonra yıkayıp bardağı kaldırırsınız, yani böyle farz ediyorum.😏İşte değişkenler de bu şekilde ihtiyaç duyulan veri kadar tanımlanır, ister tanımlandığında ister daha sonra içi doldurulur daha sonra bellek tıkanmalarını önlemek için işi bittikten sonra kaldırılmalıdır. Aslında donanıma müdahale etmediğiniz bir uygulamada artık bunları frameworkler arkada sessiz sedasız halledebiliyorlar. Yani modern diller sizin yerinize bulaşıklarınızı toplayıp ortalığı siler süpürür. Bunu kısmi olarak manuel hale getirebildiğiniz bir mekanizma da yine diller içinde bırakılmış durumdadır.

Bunlar benim rastgele bulduğum küçük örnekler; bu şekilde benzer bir sürü örnek çıkarılabilirsiniz.

Temel Veri Tipleri ve OOP ilişkisi

Aslında şu an çok kaba tabirlerle canlandırmaya çalışıyorum, yalnız introduction bölümünü bitirip nesne yönelime başlayan birisi bu benzetmemde kafa karışıklığı yaşayabilir. Eğer değişken konusuna henüz başladıysanız buraya tıklayarak devam edin, yok nesne yönelime yeni başladıysanız devam edebilirsiniz. İşin aslı; bardağın kendisi kendine has rengi, boyutları vb. özellikleri olan bir nesnedir. Beyaz renk, 5 cm. çapında, 10 cm. yüksekliğinde bir bardak olsun. Bu tanımlarda kullandığımız "beyaz renk, 5cm. çap, 10 cm. yükseklik" birer değişkene karşılık gelir. Bardağı bu şekilde ayrıştırırsanız temeline inmiş olursunuz. Yani temeli derken atomaltı da demedik, işte uygulamalarımızdaki modellemeler için ihtiyacımız olan kadar temeli. "Temel Veri Tipleri" denmesinin sebebi de budur. Nasıl ki matematikte rakamlar olmadan hiçbir işlem yapamazsak makine dili hariç yazılım dillerinde de temel veri tipleri olmadan bir iş yapamazsınız. Bu örneğim üzerinden gidersek, işler arka planda daha da ayrıntılı veya daha doğru bir ifade ile değişken tanımlama mekanizması çok daha özgür bir kullanım sağlar. Şöyle bir evde yaşadığınızı düşünün: tüm hammaddeleri ve bu hammaddeleri ihtiyacınız olduğunda işleyerek size gereken eşyayı istediğiniz fonksiyonellikte ortaya çıkaran makinelerin, o eşya ile işiniz bittiğinde yine aynı hammaddeye çeviren bir geri dönüşüm sisteminin olduğu bir ev. Değişkenler ile bardağın özelliklerini oluştur dediğinizde kullanıma hazırdır. Üstelik isterseniz içeçeği de hazır veya rengi, boyutları size özel belirlenmiş bir şekilde. Bu özellikler bu satırda .NET'e özel torpil geçersem; eğer sınıf kapsamında kullanılırsa field, kontrollü bir şekilde değer girilmesi isteniyorsa aynen İngilizce karşılığında olduğu gibi property diye geçer ki şahsen kod okunurluğu bakımından C#'ı birçok dilden avantajlı hale getirdiğini düşünürüm. Mesela javascript'e de birkaç yıl önce geldi bu yapı. Bunu sağlayan OOP ilkelerinden encapsulation dediğimiz ilkedir. Şimdilik koda girmek istemiyorum ama sadece küçücük bir örnek olması açısından...

    public class User {
        public string Name { get; set; }
    }

Yukardakine benzer bir söz dizimini kısmen Java'da da uygulayabiliriz ama bence attığımız taş ürküttüğümüz kurbağa örneğine benzer. Bunu elersek Java'da şu şekilde yazarız.

public class User {
    private String name;
    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }
}

Bu bardak mutfağın dışında başka bir yerde kullanılmasın gibi bir isteğimiz olursa da erişim belirleyici dediğimiz anahtar kelimeler devreye girer. Bu bardak ailede büyükler kullanabilsin ama çocuklar kullanamasın derseniz kalıtım ile bunu sağlayabilirsiniz. Ama aynı bardağı büyükler farklı, küçükler farklı hareket veya özelliklerde kullansın isterseniz polymorphism, su içen kişi içme esnasında farklı hareketler de yapmaya zorunlu olsun derseniz interface, içerken tek bir sözünüzle sizin istediğiniz sırada hareketleri yapsın gibi tuhaf bir isteğiniz olursa delegation kullanırsınız. Sizin istediğiniz hareketler dışında, içen kişi kendi hareketlerini belirlemek isterse, tanımladığınız delegasyonlardan uygun olanını kullanarak bir fonksiyon(event) oluşturur veya kendi oluşturduğu delegasyon üzerinden fonksiyon oluşturup bunu yapabilir. Bir tık öteye gideyim derseniz, mesela yukarıda bahsettiğim kalıtım örneğini generic sınıflar ile çok daha esnek ve özgür bir mimari sağlayabilirsiniz. Eğer sadece değişkenin ne olduğunu öğrenmek istiyor idiyseniz, yukarıdaki uyarıma rağmen bu paragrafa devam ettiyseniz çoktan pişman olmuşsunuzdur. 😄Çünkü bu bahsettiklerim nesne yönelimli programlama ile ilgilidir ve en basitleştirilmiş şekli ile bahsettim. Yoksa bu konu böyle bir paragrafta bahsedilebilecek kadar basit değil tabi ama ben ana hatlarıyla girmek için bunları yazdım. Tabi OOP öğrendiğinizde dünya önünüze serilmiyor. Hatta asıl her şey o zaman başlıyor. Bununla OOP konseptleri üzerine mimariyi düzgün kurdurmak için bir türlü yetişemeyeceğiniz prensip, yaklaşım veya metodoloji daha birçok kavram var. Örneğin bu saydıklarım her biri binlerce satır ayrıntıyla anlatılabilecek kavramlar.

Değişken Nedir

Her şeyden önce "Neden farklı veri tipleri ile çalışmaya ihtiyaç duyarız?" sorusuna bakalım. En sade cevabı; farklı veri tiplerinin bellekte kaplayacakları alan ve onlara yaptıracağımız işlemler farklıdır. Örneğin tamsayılar, metinsel ifadeler, mantıksal ifadeler ilk akla gelenlerdir. Tamsayılarla dört işlem yaptırabiliyorken, metinsel ifadeler ile bu mümkün değildir. O nedenle her programlama dilinde farklı işlemler için farklı veri tipleri bulunur. Bu şekilde hem ramde ayrılacak yerden israf etmemiş, hem de sadece o veri tipi ile yapılabilecek işlemlerin yapılmasına izin vermiş oluruz. Ayrıca bazı diller tip güvenli ya da tip denetimli dillerdir. Bu tip güvenliği, tip uyumu için tüm işlemlerin derleyici kontrolünden geçirileceği anlamına gelir. Kural dışı olan işlemler derlenmez ve derleme zamanında hata verirler. Böylece hataları önlemeye yardımcı olarak kodunuzun güvenilirliği artırılmış olur. Bu yüzden 'tipi belirlenmeyen bir değişken' gibi bir kavram, mesela C# için söz konusu değildir. Bir değişken bildirimi yapılırken o değişkenin veri tipi kesinlikle bildirilmek zorundadır. Sonrasında da o değişkene atanan veriler, belirtilen veri tipinin sınırları dışına çıkamaz. Bunun bir istisnası, bir değişkenin tuttuğu verinin başka bir tipe dönüştürülmesi(casting) işlemidir. Bu veri tipi tarafında sadece küçücük bir örnek ama bu disiplinin sağladığı (generic architecture, reflecting, refactoring vb.) pek çok avantaj vardır. Fakat örneğin web uygulamarında başka alternatifiniz olmayan JavaScript derleyicisi bu denetimi yapmaz. Yani bu yazıyı okurken tarayıcı konsolunu açıp aşağıdaki kod satırlarını yapıştırırsanız herhangi bir hata vermeden derlenip sonuç yazılacaktır.

var name = "gürkan";
var number = 'A' + 1;//HEX A1 = DECIMAL 161
var number2 = '2' + 1;//Output: 21
console.log(name);

name = 123;
console.log(name);
console.log(number);

name değişkenine 123 atanmasına rağmen hata vermeden derlenir. console.log() fonksiyonunu Console.WriteLine() metodu ile değiştirerek C# için alacağınız derleme hatasını görebilirsiniz. Bu denetim, bir eksik veya JavaScript'in dezavantajı değil; dilin yaklaşımı böyledir.

Kısacası değişken, ramde belirli tipten bir veriyi tutması için ayrılan adrestir. İhtiyaç duyulan değerleri ramde geçici olarak saklamak amacı ile kullanılırlar. Her değişkene, ramde o değişkenin tutacağı veri tipine karşılık gelecek kadar bir yer ayrılır. Bu yere "değişkenin adresi" denir. Adresi olduğu gibi her değişkenin bir adı da vardır. Bu ad kullanılarak; değişkene değer atanabilir, tekrar okunabilir veya değiştirilebilir. İstendiğinde bazı kısıtlamalar getirilebilmekle birlikte; bir değişkene erişebilmek demek bu 3 isteği yerine getirebilme yeteneklerine sahip olmaktır.

Değer Tipleri ve Referans Tipleri

Nesne yönelim yaklaşımında veri tipleri, değer tipi ve referans tipi olmak üzere iki genel kategoride toplanır. Bu iki tip arasındaki fark, bir değişkenin ne içerdiği ile ilgilidir. Değer tipinde bir değişken 10 ya da 10,05 gibi gerçek bir değer içerirken; referans tipi bir değişken, o değere yapılan referansı içerir. C diline aşinalığınız varsa pointer mantığının benzeridir. Değer tipleri belleğin stack(yığın) bölümünde çalışırken referans tipleri heap(öbek) bölümünde çalışır. Basit kalmaya çalışıyorum, eğer biraz daha derine ineyim derseniz belleğin bu bölümlerini araştırabilirsiniz.

Stack vs Heap

Temel tiplerden bir referans tip oluştururuz ve bu tip çağrıldığında bir nevi paketlenip bütün olarak önümüze geldiği için, heap daha yavaş çalışır. Ama hız dışındaki asıl büyük fark; değer tipleri veri aktarımında kopyalama yaparken, referans tiplerden oluşturulan örneklerin tümü ram üzerinde tek bir adresi işaret eder. Bu yüzden birinde yapılan değişiklik tümünü etkileyecektir. Eğer bunu gerektiği gibi kavramayazsanız tehlikeli sularda geziyorsunuz demektir!

Stack vs Heap

Temel Veri Tipleri Tablosu

What and where are the stack and heap?