Bu yazı; veri tipi nedir, doğru kullanımı nasıl olmalı veya buna neden ihtiyaç duyarız gibi soruların kısa cevaplarını içerir. Eğer farklı dillerin temel veri tiplerinin isim ve boyut aralıklarını arıyorsanız Temel Veri Tipleri başlıklı yazıda bulabilirsiniz.

Adı üstünde! 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 tanıştığımız, büyük üstat Ömer Hayyam ile öne çıkan X'e karşılık gelir diyebiliriz. Yani değişken kavramı direk yazılımla ilgili değil; aslen cebirle birlikte fizik, kimya vb. diğer sayısal bilimler için geçerli bir kavramdır. Örneğin fizikte yapılan deneyde ölçülen basınç, sıcaklık, hız, sürtünme gibi değerler birer değişkene karşılık gelebilir. 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. 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; iki 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ırakmayalım. Yaptığımız işlemlerle birlikte aynı zamanda random access memory(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(1856-1943), 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🏭muhtemelen yüksek gerilime ihtiyacınız olacaktır. Evimize kadar gelen alternatif akım, büyük abilerine nazaran evimizdeki küçük trafolar ve bazı küçük komponentlerle -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 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 kesinlikle 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ılabilir.

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" gbi değerler birer değişkene karşılık gelir. Bardağı bu şekilde ayrıştırırsanız temeline inmiş olursunuz. Yani temeli derken atom altı 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ş yapamayı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 gereken eşyayı, istediğiniz fonksiyonellikte ortaya çıkaran makinelerin ve o eşya ile işiniz bittiğinde yine aynı hammaddeye çeviren bir geri dönüşüm sisteminin olduğu bir ev. 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 diline 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ğaya değmez" örneğine benzer; 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 inheritance 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 function(event) oluşturur veya kendi oluşturduğu delegation üzerinden function 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 istiyorduysanız, yukarıdaki uyarıma rağmen bu paragrafa devam ettiyseniz çoktan pişman olmuşsunuzdur.😄Çünkü bu bahsettiklerim nesne yönelimli programlama ile ilgilidir; bütünüyle kavramak uzunca bir emek ve devamında oturması için süre ister. Ben en basitleştirilmiş şekli ile bahsettim. Yoksa bu konu böyle bir paragrafta bahsedilebilecek kadar basit değil.

Tabi OOP öğrendiğinizde dünya önünüze serilmez; hatta asıl her şey o zaman başlar. Bununla sadece OOP konseptleri üzerine mimariyi düzgün kurabilmek için bir türlü yetişemeyeceğiniz prensip, yaklaşım veya metodoloji gibi birçok kavramın olduğu farklı bir dünyanın kapısını aralarsınız. Duruma ve şartlara göre hangisinin gerekli hangisinin gereksiz olduğunu ancak bundan sonra gerçek manaysıyla anlamaya başlarsınız. Sonuç olarak, bu "harikalar diyarı"ndan içeriye girdikten sonra tadını çıkarmak da korkup kaçmak da size kalmış.

Neden İhtiyaç Duyarız?

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, tip güvenliği olan diller 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; var anahtar kelimesi ile derleyici tipi kendisi belirleyebilir. 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 uygulamaları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. Bu yüzden sonradan dile eklenen let, const gib ianahtar kelimeler bulunur.

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; ihtiyaç 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 üç isteği yerine getirebilme yeteneklerine sahip olmaktır.

Değer Tipi ve Referans Tipi⛓

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 1 ya da 1,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 aynısı değil ancak bir benzeridir. Değer tipleri belleğin stack bölümünde saklanırken referans tipleri heap bölümünde saklanır.

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 şekilde tam olarak kavramayazsanız tehlikeli sularda geziyorsunuz demektir!

Stack vs Heap

Temel Veri Tipleri TablosuWhat and where are the stack and heap?