Temel C Programlama -12- C Veri ve Değişken Tipleri

Veri tipleri ve bunlar arasındaki ilişki programcılıkta çok önemli bir nokta olduğu için bunu ilk konularda anlatma kararı aldım. Bu başlıkta C’deki veri ve değişken tiplerini bütün ayrıntısıyla anlatmaya çalışacağız. Daha önceki örneklerde int ile tam sayı değişkeni tanımlamış ve char ile karakter değişkeni tanımlayıp kullanmıştık. Bunları dağınık halde öğrenmeniz yerine tablo halinde ne kadar değişken var yok öğrenip bunlara bakarak kullanmanızın zamanı gelmiştir. İlerleyen örneklerde örneklerin yanında kendi programlarınızı yazabilmenin yolu referansı okuyabilmekten geçer. O yüzden derslerin bir referans niteliğinde olmasını hedefliyoruz.

Değişkenleri, değişken tanımlamayı ve değerleri iyi anlamak program yazmanın en önemli kısımlarından biridir. Bunları iyi anladığınız sürece karşılaşacağınız sorunlar azalır ve yazdığınız programların kalitesi artar. Değişkenler sadece int değişkenadı; şeklinde adlandırdığınız ve içerisine tamsayı koyduğunuz yapılar değildir. Pek çok değer ve değişken türü bulunmakadır. Bu sabit değerleri kullanabilmek için de yine değişkenleri kullanabilmek gereklidir.

Sabit değer dediğimizde ‘a’, 500, “Merhaba Dunya!” değerlerini söyleyebiliriz. Bunlar sabit değer olarak bir değişikliğe uğrayamaz ve programcı tarafından yazılır. Bu sabit değerleri saklayabilmenin ve üzerinde işlem yapabilmenin yolu da belleğe aktarmaktır. Değişkenler bellek alanında adlandırılmış bölgelerdir ve bu bölgelere bu değerler aktarılmaktadır. Artık bu bölgelerin adını çağırdığımızda o bölgelerdeki değerler bize gelmektedir. Bellekteki değişkenlerin konumunu şöyle bir tablo yaparak size gösterebilirim.

Adres

0x01

0x02

0x03

0x04

0x05

Değişken

int toplam_deger

int sonuc_degeri

int x

long uzun_bir_sayi

char a

Değer

500

100

25000

500000000

‘b’

Burada gördüğünüz gibi değişkenler değerlere verilen isim değil adres değerlerine verilen isimlerdir. Örneğin toplam_deger olarak adlandırdığımız değişkenin adres değeri 0x01 olduğu için program 0x01 adres hücresindeki veriyi okuyup bize vermektedir. Bu adresleri ve adreslerdeki veriyi tek tek bizim ezberleyip kullanmamıza gerek yoktur. Onun yerine keyfimize göre değişkenler tanımlayıp değişkenler üzerinde rahatça işlem yapabiliriz. Assembly dilinde değişkenler olmadığı için adresler ve sayısal değerler üzerinde işlem yapmamız gerekir ve bu oldukça kafa karıştırıcı olabilir. Bizim değişkenleri hafızada yer tutulan yerlerin adları olarak özetleyip böyle anlamamız gereklidir.

Değişkenleri en önemli noktadan özet bir şekilde sizlere anlattığımıza inanıyoruz. Şimdi C dilinde bu değişkenlerin nasıl olduğuna ve nasıl tanımlandığına bir bakalım. Öncelikle en temel değişkenler tam sayı tipinde olup belli değer aralığına sahiptir. Burada farklılaşma işaretli olup olmadığına yani eksi değer alıp almadığına göre ve hangi aralıkta değer alabileceğine göredir. Aşağıdaki tablodan temel değişken yapılarını inceleyelim.

Ad

Açıklama

Format İşareti

char

En küçük adreslenebilir değişken tipidir. 8-bitlik genişlikte olup -127, +127 arası değer alır.

%c

signed char

char değişkeni ile aynı olsa da bunun işaretli olduğunu garanti altına almaktadır. C dilinde normalde eğer işaretsiz değer olarak belirtmiyorsak işaretli olarak tanımlamaktadır. Bu değer de -127 ve +127 arası onluk sayı değeri alabilir. Karakterler de sayı değeri olduğu için ve 8-bitlik ASCII formatında olduğundan dolayı char değişkeni karakter depolama maksadıyla kullanılmaktadır.

%c

unsigned char

char ile aynı boyutta olsa da işaretsiz sayıları bulundurduğundan 0-255 arası değer alabilir. Bu durumda genişletilmiş ASCII karakterleri kullanacaksak bunu kullanmamız gereklidir. Çünkü ASCII yani harf, rakam, sembol gibi karakterlerde negatif değer yoktur.

%c

short

short int

signed short

signed short int

Kısa tam sayı değeridir. Short kısa anlamı taşıdığı için normal int değerine göre daha az bellek alanı tutmaktadır. 16-bitlik değer işaretli olarak -32767 ve +32767 arası değer tutmaktadır. Normalde int kullansak da daha az yer kaplamasını istediğimiz durumlarda bunu kullanabiliriz.

%hi

unsigned short

unsigned short int

Kısa işaretsiz tamsayı değeridir. 0 – 65535 arası tam sayı değer alır. 8-bit mikrodenetleyiciler üzerinde çalışırken int değerinin karşılığı budur. Ama 32-bit temelli bir sistemde bunu short diye belirtmek gerekir.

%hu

int

signed

signed int

Temel işaretli tam sayı tipidir. Normalde yukarıdaki gibi -32767 ve +32767 değerleri arasında olsa da 32-bit sistemlerde -2 147 483 648 ve + 2 147 843 647 arasında değer almaktadır. Bu değer 32-bit genişliğindedir ve çoğu tam sayı işinde bize oldukça yeterli olmaktadır.

%i ya da %d

unsigned

unsigned int

Eğer işaretli tam sayı değişkeni olan int değişkeni yeterli olmazsa bunun işaretsiz versiyonunu kullanabiliriz. Bu durumda değer 0 ile 4 294 967 295 arasında olacaktır.

%u

long

long int

signed long

signed long int

32 bitlik işaretli tam sayı değişkenidir ve -2 147 483 648 ile + 2 147 483 647 arasında değer almaktadır. 32-bit derleyicilerde int ile aynı değeri alsa da farklı bir sistemde bu değer için long ifadesini kullanmak gerekebilir. int ifadesi sisteme göre değişkenlik göstermektedir.

%li

unsigned long

unsigned long int

Bilgisayarda unsigned int ile aynıdır ve 4 baytlık bir adres uzunluğuna sahiptir. 0 ile 4 294 967 295 arası değeri bulundurur.

%lu

long long

long long int

signed long long

signed long long int

Bu sefer gerçekten uzun sayıları barındırabilecek bir değişken görüyoruz. Bu değişken 64 bitlik bir genişliğe sahiptir. Bu da toplamda 8 baytlık bir alan demektir. C99 standartı ile literatüre girmiştir. -9 223 372 036 854 775 807 ile +9 223 372 036 854 775 807 arasında değer bulundurabilir.

%lli

unsigned long long

unsigned long long int

Bu long long tipindeki değişkenin işaretsiz halidir. Böylelikle kapasitesi pozitif değerlerde ikiye katlanmaktadır. 0 ile +18 446 744 093 709 551 615 arası değer saklayabilir.

%llu

Görüldüğü gibi bu veri tipleri oldukça zengin ve istediğimiz tamsayı değerini saklamada kapasite bakımından sıkıntı yaşatmaz. Program yazarken değişken tipini iyi seçmek her zaman için çok önemlidir. Yüksek kapasite değişkenleri gereksiz yere kullanmak bellekte gereksiz yere yer tutulmasına ve performans kaybına sebep olacaktır. Yetersiz değişken kullanmak da azami değerler konusunda sıkıntı çıkaracaktır. Örneğin bir neslin yakından tanıdığı Knight Online oyunu üzerinden örnek verelim. Bu oyunda envanterimizde veya kasada toplam 2 147 483 648 oyun parası saklayabiliyorduk. Bu değer size tanıdık gelmelidir. Bu oyun parası oyunun ekonomisinden dolayı bir dönem oldukça komik bir rakam haline gelmişti. Oyun parasının değeri olmayınca 10 milyar, 20 milyarlık eşyaların alışverişini ancak gerçek para ile yapabiliyorlardı. Böyle bir hatanın tek sebebi de geliştiricinin int oyun_parasi; şeklinde oyun parası değerini tanımlamasıdır. Oyun parası negatif değer almadığı halde neden negatif değer alan bir değişken kullanılsın? Eğer işaretsiz bir değişken kullanılsaydı kapasite iki kat artacaktı. Eğer long long cinsinden bir değişken kullanılsaydı hiç kapasite sorunu yaşanmayacaktı.

Tabloda sağ tarafta gördüğünüz format işareti printf fonksiyonundan bize tanıdık gelecektir. Biz her değişken tipini yazdırırken formatına uygun olarak yazdırıyorduk. Burada da eğer printf, scanf, sprintf gibi “formatlı” fonksiyonları kullanacaksak bu format işaretlerini bilmemiz gerekir. Bu giriş ve çıkış fonksiyonlarını ayrıntısıyla anlatacağım için daha sonra bu konuya tekrar döneceğiz. C dilinde veri tipleri bitmese de tam sayı tiplerini bu yazıda bitirelim. En son olarak bir uygulama yapalım ve bu değişkenleri sahada görelim.

#include <stdio.h>
#include <stdlib.h>
int main() {
   char karakter = 'A';
   unsigned char genis_karakter = 219; // Kutu İşareti
   int sayi_1 = -50000;
   int sayi_2 = 600000;
   int sayi_3 = 3000000000;
   unsigned int u_sayi_3 = 3000000000;
   long long uzun_sayi = 9223372036854775807;

   printf("Sayilarin Gosterildigi Programa Hos Geldiniz\n");
   printf("*********************************************\n");
   printf("Birinci Sayimiz Karakter Olarak :  %c \n", karakter);
   printf("Birinci Sayimiz Sayi Olarak :  %i \n", karakter);
   printf("Ikinci Sayimiz Karakter Olarak :  %c \n", genis_karakter);
   printf("Ikinci Sayimiz Sayi Olarak :  %i \n", genis_karakter);
   printf("Ucuncu Sayimiz: %i \n", sayi_1);
   printf("Dorduncu Sayimiz: %i \n" , sayi_2);
   printf("Besinci Sayimiz: %i \n", sayi_3);
   printf("Altinci Sayimiz: %i \n", u_sayi_3);
   printf("Cok Uzun Sayimiz: %I64d \n \n \n", uzun_sayi);




 system("PAUSE");
 } // Main Sonu

Program çalıştığında konsol ekranının çıktısı şu şekilde olacaktır.

Konsol ekranında oldukça ilginç çıkışları görmekteyiz. Bazı çıkışların bizim yazdığımız değerlerle alakası yok. Öncelikle oluşturduğumuz değişkenlere ve bunlara atadığımız değerlere bakalım.

char karakter = ‘A’;

Burada karakter adlı char değişkenine ‘A’ değerini atadık. Bu sayısal bir değer olmasına rağmen harf değeri nasıl alıyor?, diye soracak olursanız bilgisayar sistemlerinde her harfin bir sayısal karşılığı vardır cevabını veririz. ASCII tablosuna baktığımızda A’nın 65 değerinin olduğunu görmekteyiz. Yani biz elle 65 yazsak da eğer karakter formatında yazdırmak istiyorsak bilgisayar bunu A olarak yazdıracaktır.

unsigned char genis_karakter = 219; // Kutu İşareti

Normalde harfler ve rakamlar ASCII standartında 0 ile 127 arasındadır. Bu klavyedeki bütün rakam, harf ve sembollerin yanında bazı komutları da içinde bulunduracak kadar geniştir. Fakat iş başka dillere geldiğinde bu özel harfleri gösterecek değer yoktur. O yüzden Genişletilmiş ASCII ve Unicode kodlaması getirilmiştir. Biz unsigned char diye bir değer tanımlayarak genişletilmiş ASCII harflerini kullanabiliriz. Biz burada ilginç bir şekil olan kutu şeklini kullanmak istiyoruz. Klavyemizde kutu işaretli bir tuş olmadığından bunun rakamsal değeri olan 219’u yazacağız.

int sayi_1 = -50000;

Bu komutta int değişken tipinin negatif yani işaretli sayı alabildiğini göstermek için sayi_1 değişkeni tanımlayıp ilk değerini -50000 olarak verdik.

int sayi_2 = 600000;

Bu komutta int değişken tipine sıradan bir sayı değeri verdik.

int sayi_3 = 3000000000;

İşte bu nokta çok önemli. Çünkü biz burada int değişken tipine kapasitesinden fazla değer veriyoruz. Bu değişken tipine kapasitesinden fazla değeri vermenin sonucu ne olabilir?, sorusuna cevabı ise aşağıda göreceğiz.

unsigned int u_sayi_3 = 3000000000;

Burada yukarıda tanımladığımız değişkene aktardığımız aynı değeri unsigned yani işaretsiz tipine aktarıyoruz. Doğru değişken tipi seçmenin önemini konsol ekranın incelerken göreceğiz.

long long uzun_sayi = 9223372036854775807;

Burada ise çok uzun bir sayıyı değişkene aktarıp göstermek için böyle bir değişken tanımladık ve kendisine çok çok uzun bir sayı yükledik. Şimdi bir yandan printf fonksiyonlarını inceleyelim bir yandan da konsol ekranına satır satır bakalım.

printf(“Birinci Sayimiz Karakter Olarak : %c \n”, karakter);

İlk fonksiyonda birinci sayıyı göstermek için karakter değişkenini %c formatı ile yazdırdık. Normalde tamsayı yazdırmak için %i formatını kullanıyorduk. %c formatı ile yazdırdığımız için ekranda “A” harfi olarak göründü.

printf(“Birinci Sayimiz Sayi Olarak : %i \n”, karakter);

Burada yine karakter değişkenini farklı bir formatla yazdırdık. Tam sayı formatında yazdırdığımızda yine sayısal değerini görme imkanımızın olduğunu gösterdik. Ekranda 65 değeri olarak “A” karakterinin sayısal değerini görüyoruz.

printf(“Ikinci Sayimiz Karakter Olarak : %c \n”, genis_karakter);

Bu sefer ekranda genis_karakter değişkenini %c formatında yani ASCII olarak yazdırdık. Ekranda bir kutu görmekteyiz. Böyle sembolleri kullanarak ilginç işler yapılabilir. Hatta ASCII sembolleriyle uzun yıllardır oyun yapılmaktadır.

Resim: ASCII Karakterleri ile görselleştirilmiş bir rol yapma oyunu

printf(“Ikinci Sayimiz Sayi Olarak : %i \n”, genis_karakter);

Burada yukarıda tanımladığımız “kutu” karakterinin sayısal değerini görmekteyiz. Değişken üzerinde bir değişiklik olmadı sadece formatı %c’den %i olarak değiştirdik.

printf(“Ucuncu Sayimiz: %i \n”, sayi_1);

Burada olağan bir işaretli sayı yazdırma işlemini görmekteyiz.

printf(“Dorduncu Sayimiz: %i \n” , sayi_2);

Burada da yine bir int değerini sınırları içerisinde yazdırdık. Daha önceki örneklerden alışkın olduğumuz için şimdi anlatacağımız bir şey kalmadı.

printf(“Besinci Sayimiz: %i \n”, sayi_3);

Burada ise işler değişmektedir. Biz bu sayıya “3000000000” değerini versek de bu sayı ekranda -1294967296 olarak gözükmektedir. Küçük bir dikkatsizliğin nasıl bir sonucu olabileceğini buradan görebilirsiniz. Peki bu değer niçin böyle yazılmaktadır?, sorusuna cevabı burada verelim.

Öncelikle bilgisayarlarda negatif bir değer yoktur. Negatif değerleri temsil eden 1 ve 0 değerleri vardır. Bir değişken eğer işaretsiz olarak tanımlanırsa bütün bitlerini sayı depolamak için kullanır. Eğer işaretli yani negatif değer alırsa sayının negatif mi pozitif mi olduğunu belirtmek için bir işaret bitine ihtiyaç duyar. Bu da en sol taraftaki bit olmaktadır. Yani en sol bit “1” ise sayı negatif “0” ise pozitiftir. Negatiften pozitife çevirme işi de bu bitin değerini çevirmekle olur. Biz bu değişkene 3000000000 değerini yazmakla değişkene ikilik tabanda 10110010110100000101111000000000 değerini yazmış oluruz. 32-bitlik bu değerin en başında 1 sayısı var değil mi ? O halde program bunu negatif sayı olarak değerlendirecektir ve geri kalan değeri okuyacaktır. Çünkü biz değişkeni tanımlarken bunun işaretsiz olduğunu programa belirtmedik. Geri kalan değeri okuduğunda ise -1294967296 sayısını bulacaktır. Program aslında gayet doğru çalışmaktadır burada bizim yaptığımız hata söz konusudur. Bunun sebebini bilmek bayağı ileri seviye bir bilgi olsa da şimdiden size anlatalım dedik.

printf(“Altinci Sayimiz: %i \n”, u_sayi_3);

Burada unsigned int olarak tanımladığımız değişkenin değeri olması gerektiği gibi gözüküyor. Değer aralıklarına dikkat etmenin önemini uygulama üzerinde görüyoruz.

printf(“Cok Uzun Sayimiz: %I64d \n \n \n”, uzun_sayi);

Burada bu çok uzun sayıyı yazdırmak için neden %I64d formatını kullandığımı sorabilirsiniz. Yukarıda %lli anlatsak da bizim kullandığımız MinGW derleyicisi maalesef bunu desteklememektedir. O yüzden bu derleyicinin anlayacağı dildeki parametreyi araştırıp bulduk. Standartlara uygun kod yazsak da derleyici tabanlı bir sorunla da bayağı erken karşılaşmış olduk. Öğrenme aşamasında karşılaşılan sorunlar ve hataları öğrenmek ve bunlara çözüm getirmek çok önemlidir. Çünkü hiçbir zaman öğrenirken her şey dört dörtlük ilerlemez. Öğrenci tek başına araştırma yaparak ve deneyerek hatanın çözümünü aramak zorundadır. Eğer hatalardan bunalıyorsanız bu sektörde çalışmak sizin için işkence gibi olacaktır. Çünkü hangi noktada olursanız olun hatalarla sürekli yüzleşmeniz gerekecektir.

Bir sonraki başlıkta veri tiplerine devam edeceğiz.

Last updated