plazma - amatör bilgisayar kültürü

C++ Kursu 2

Bilgem 'Nightlord' Çakır

Herkese merhabalar. Geçen sayıda başlayan kursumuzun ilk bölümünde, C++ programlarının iskeletinden ve değişkenlerden bahsetmiştik. Değişkenlerin en önemli kullanım alanlarından biri kullanıcıdan gelen girdileri saklamaktır. Şimdi kullanıcıdan nasıl girdi alacağımıza bakalım.

1. Kullanıcıdan Girdi Almak

Bu noktada malesef yine yazı yazdırma komutundaki gibi şimdilik ezberci bir yaklaşım takınmanızı rica edeceğim. Söz veriyorum daha ileri bölümlerde std::cout ve std::cin hakkında daha ayrıntılı bilgi vereceğim. Şimdi aşağıdaki koda bir göz atın:

#include <iostream>

int main()
{
  std::cout << “Toplama Programina Hosgeldiniz”
    << std::endl;

  // toplanacak birinci sayiyi oku
  int birinciSayi;
  std::cout << “Birinci sayi: ”;
  std::cin >> birinciSayi;

  // toplanacak ikinci sayiyi oku
  int ikinciSayi;
  std::cout << “Ikinci sayi: ”;
  std::cin >> ikinciSayi;

  // sonucu hesapla ve goster
  std::cout << “Sonuc = ”
    << birinciSayi + ikinciSayi
    << std::endl;
  return 0;
}

Bu programda daha önce görmediğiniz yeni şeylere teker teker değinelim. İlk etapta dikkatinizi çekebilecek şey yedinci satır. Bu satır bir yorum satırı. C++' ta yorum satırları // ile başlar ve satır sonunda biter. Yorum satırlarını kodu okuyan kişilere yardımcı olması amacı ile koda ekleriz. Çoğu zaman birbiri ile alakalı ve bir bütünlük içeren birkaç satırı başlarına bir yorum satırı koyup açıklamak iyi bir alışkanlıktır.

Kullanıcıdan girdi alan satırlar tahmin edeceğiniz üzere std::cin ile başlayan satırlar. Burada dediğim gibi şu an anlamanız gereken şey;

std::cin >> degisken

şeklindeki bir komutun konsol ekranında klavye ile girilip enter'a basılarak verilen kullanıcı girişlerini satırda adı geçen değişkene yüklediği. Şimdilik burada bırakalım bu konuyu.

Son bir dikkat edeceğiniz nokta da std::cout satırlarının bazılarının sonunda std::endl olmayışı. Yeri gelmişken std::endl nesnesini tanıtalım. Bu nesne yine standart kütüphanede tanımlı olan bir nesne. Bu nesneyi std::cout satırlarında kullandığımız zaman yazı yazdırma komutumuzda yeni satıra geçmeye sebep oluyoruz. Burada kullanmadığımız yerlere bakarsanız, kullanıcıdan giriş gelen yerleri seçtiğimizi göreceksiniz. Burada ekrana “Birinci sayı: ” yazıp ardından std::endl kullanmayarak konsol penceresinde cursor'un aynı satırda kalmasını sağlamış oluyoruz. Bu sadece görsel bir detay ve çok önemli değil.

2. Bir Başka Tip: std::string

C++ standart kütüphanesinde bize sunulan en önemli tiplerden biri de std::string tipidir. Bu tipte tanımladığımız değişkenlerin içinde her uzunlukta, küçük veya büyük yazı verilerini saklayabiliriz. Bu değişkenlerin içinde arama yapmaktan tutun da, içlerindeki yazıları kesip biçmek veya birleştirmek gibi bin bir türlü işlem std::string tipi ile elimizin altındadır.

Daha sonra tanıyacağımız pekçok standart kütüphane tipinde olduğu gibi std::string kullanabilmek için programımızın başına bir #include satırı eklemek gerekiyor. Henüz bu satırların tam olarak ne yaptığını bilmeseniz de neye yaradığını tahmin etmeye başlıyor olabilirsiniz. Tıpkı std::cout ve std::cin kullanabilmek için #include <iostream> dememiz gerektiği gibi, std::string tipini kullanabilmek için de #include <string> satırını kullanmamız gerekiyor. Dolayısıyla #include satırlarının programımıza daha önceden yazılmış bazı kodları kullanabilme imkanı eklediğini görebilirsiniz. Bu konuya daha detaylı olarak sonra eğileceğiz. Şimdi aşağıdaki kodu inceleyelim:

#include <iostream>
#include <string>

int main()
{
  std::cout << “Adiniz nedir? ”;
  std::string isim;
  std::cin >> isim;
  std::cout << “Merhaba ”
    << isim << std::endl;
  return 0;
}

Burada onceki örnekten farklı olarak, isim adında ve std::string tipinde bir değişken tanımlıyoruz. Daha sonra, std::cin satırını kullanarak, kullanıcıdan girdi alıp bunu isim değişkenine saklıyoruz. Daha sonra da std::cout satırında bu isim değerini 'Merhaba' yazısı ile beraber kullanarak bir mesaj gösteriyoruz ekranda.

Std::cin komutunu burada olduğu gibi std::string tipinden bir değişken ile kullandığımızda, kullanıcı konsol ekranında enter tuşuna basana kadar girdiği karakterlere bakılacak ve ilk boşluk karakterine kadar olan bölüm değişkenimize yüklenecek. Örneğin kullanıcı konsoldan,

Ahmet Ahmetoglu

girse, isim değişkenine Ahmet değeri yüklenecek. Birazdan bunun nasıl çalıştığını anlatacağız.

3. Operatörler

Bu noktada operatörler ile tanışma zamanı geldi. C++'da operatörler değişkenler ve sabitler üzerinde işlemler yaparlar. Genelde operatörler bir veya iki argümanı alırlar, bunları kullanarak bir işlem yapar ve genellikle bir başka değeri döndürürler. Örneğin;

toplamSkor = level1Skor + bonusSkor;

gibi bir kod satırına bakalım. Bu satırda iki tane operatör var. Bunlar + ve = operatörleri. + operatörü level1Skor ve bonusSkor adlı iki değişkeni argüman olarak alıyor, bunlar üzerinde toplama işlemi yapıp sonucu döndürüyor. Ardından = operatörü de toplamSkor ve toplama işleminden dönen sonucu iki argüman olarak alıyor, soldaki argümana sağdaki argümanın değerini atıyor.

Dikkat edeceğiniz bir nokta bir operatörün işlemini yaptıktan sonra geri değer döndürmesi. Adeta operatör işlem yaptığı iki argüman ile beraber satırdan silinip yerine döndürülen değer yazılmış gibi devam ediyor programın çalışması. Yani yukarıdaki satırda toplama işleminin sonucu döndükten sonra + operatörü ve onun iki argümanı yok oluyor ve onların yerine işlemden dönen sonuç tek bir geçici değer olarak kalıyor. Başka bir deyişle yukarıdaki satır toplama işleminden sonra aşağıdaki satıra indirgeniyor

toplamSkor = ToplamaIslemindenDonenDeger;

Başka aklınıza gelebilecek operatörler +, -, *, /, &, |, <, >, <<, >> ve daha birçokları. Operatörlerden daha ileriki bölümlerde çok daha detaylı bahsedeceğiz. Ama buraya kadar anlattıklarımız hemen şimdi anlatacağımız bölüm için gerekli idi.

3.1. std::cout ve << operatörü

Konsola birşeyler yazdırmak için kullandığımız std::cout objesini biraz biraz tanıdınız. Şimdi bu mekanizma ile ilgili biraz daha bilgi edineceksiniz.

Kursun bir sonraki bölümünde sınıflar ve nesneler ile detaylı olarak tanışacaksınız. Ancak bu noktada std::cout'un aslında standart kütüphanede tanımlı bir nesne olduğundan bahsetme zamanı geldi. Bu noktaya kadar ezbere kullanmakta olduğunuz, yazı yazdırma satırının gerçekte nasıl çalıştığı ile ilgili bazı yeni şeyler öğreneceksiniz şimdi.

std::cout << “Batsin bu dunya”;

Bu komutu ilk örneğimizde kullanmıştık. Burada aslında << bir operatör ve iki argüman alıyor. Birinci argüman std::cout nesnesi. Ikinci argüman ise “Batsin bu dunya” sabiti. Burada kullanıldığı şekliyle << operatörü yine standart kütüphanede tanımlı ve yaptığı iş sağdaki argümanı alıp soldaki argümana yazdırmak. Std::cout yine standart kütüphanede tanımlı olan ve kendisine << operatörü ile geçirilen harfleri ekrana gösterilmek üzere gönderen bir nesne. Dolayısıyla << operatörü bu şekilde bir işlemi aldığı iki argüman üzerinde yaptıktan sonra solundaki argümanı geri döndürüyor. Yani bu örnekte std::cout nesnesini. Bunun ne demek olduğuna biraz daha yakından bakalım.

std::cout << “Batsin bu dunya”
  << std::endl;

Bu satır çalışırken öncelikle soldaki << operatörü çalışıyor. Yani ilk önce “Batsin bu dunya” yazısı std::cout nesnesine gönderilip ardından sonuç olarak std::cout nesnesi döndürülüyor (ki bu esnada yazı konsol ekranına yazdırılmış durumda). Yukarıda anlattığımız şekilde bu noktada satır aşağıda gördüğünüz hale indirgeniyor.

std::cout << std::endl;

Ardından bu işlem çalıştğında da std::endl nesnesi std::cout nesnesine << operatörü aracılığı ile geçirilmiş oluyor.

Dolayısıyla bir satırda başa std::cout koyup ardından aralarına << operatörünü koymak koşuluyla istediğimiz kadar değişkeni veya sabiti ekrana yazdırabiliriz. Bu yüzden yine önceki örneklerde gördüğünüz

std::cout << “Merhaba ”
  << isim << std::endl;

satırı çalışabiliyor.

3.2. std::cin ve >> operatörü

Benzer şekilde std::cin ve >> operatörünü kullanarak bir satırda birden fazla değişkene değer yüklemek de mümkün.

std::string isim;
int yas;
std::cin >> isim >> yas;

Bu örnekte kullanıcı konsolda şu satırı girip enter'a basarsa;

Ahmet 25 <enter>

Bu durumda std::cin nesnesi konsoldan girilen karakterleri taramaya başlar ve sonunda ilk boşluk karakterine kadar olan bölümü yani 'Ahmet' kelimesini isim değişkenine yükler. Ardından std::cin nesnesini döndürür. Satırımız şuna indirgenmiş olur:

std::cin >> yas;

Bu noktada std::cin nesnesinde taranacak karakterler olarak sadece 2 ve 5 karakterleri kalmıştır. Bu karakterleri taramadan önce yas değişkeninin tipine bakılır. Değişken tamsayı tipinde olduğu için bu tarama esnasında 25 sayısı yas değişkenine yüklenir.

Dikkat ederseniz, hem (std::cin ve <<) hem (std::cout ve >>) gayet 'akıllı' işlemler. Yani kendilerine verilen argümanların tiplerine göre farklı işlemler yapabiliyorlar. Bu aklın nasıl çalıştığını daha ileride operatörlerden daha detaylı bahsettiğimiz esnada anlayacaksınız.

4. En Önemli Komut: if

Bütün programlama dillerinde karar mekanizmaları yaratabilmenin ilk yolu koşullu dallanmadır. C++' da bu işe yarayan komut da if komutudur. Bu komutu kullanarak programımızın çeşitli bölümlerinde, içinde bulunulan duruma göre farklı davranabilme imkanımız olur. Hemen aşağıdaki örneğe bir bakın.

#include <iostream>

int main()
{
  std::cout << “Issiz bir magarada ilerliyorsun. ”
    << “Ileride bir citirti duydun. Ne yapacaksin?”
    << std::endl;
    << “1- Kilicimi cekecegim” << std::endl
    << “2- Aydinlatma buyusunu yapacagim”
    << std::endl;

  int komut;
  std::cin >> komut;

  if ( komut == 1 )
  {
    // kilici cekti
    std::cout << “Cok guclu bir canavar sana dogru atladi ”
      << “ve sen birsey yapamadan seni oldurdu” << std::endl;
  }
  else if ( komut == 2 )
  {
    // buyu yapti
    std::cout << “Cok guclu bir canavar gordun. ”
      << “Fakat canavar aydinliktan korktu ve kacti”
      << std::endl;
  }
  else
  {
    // taninmayan komut
    std::cout << “Komut anlasilamadi”
      << std::endl;
  }
  return 0;
}

Tamam kabul ediyorum biraz küçük ama yine de ilk text adventure oyununuz için fena sayılmaz :)

If komutunun en basit formunu anlatarak başlayalım

if ( kosul )
{
  komut;
  komut;
  ...
}

Burada koşul doğru ise if komutunun hemen sonrasından gelen kod bloğu çalıştırılıyor (kod bloklarından geçen bölümde bahsetmiştik). Eğer koşul yanlış ise bu kod bloğu çalıştırılmadan atlanıyor. If komutunun bir başka formu ise şöyle

if ( kosul )
{
  komut;
  komut;
  ...
}
else
{
  komut;
  komut;
  ...
}

Bu formda kullanıldığında ise iki kod bloğundan yalnızca biri çalıştırılıyor. Koşul doğru ise birinci kod bloğu çalışırken, yanlış ise ikinci kod bloğu çalışıyor. Bunun yanında if komutunu bazen de şu şekilde kullanabiliyoruz

if ( kosul1 )
{
  komut;
  komut;
  ...
}
else if ( kosul2 )
{
  komut;
  komut;
  ...
}

Böyle kullandığımızda iki farklı koşulu test etmiş oluyoruz. Koşul1 ve koşul2nin doğruluk durumlarına göre bu kod bloklarından biri çalışabiliyor. Eğer koşul1 doğru ise, birinci blok çalışıyor ve diğer else if bölümlerine bakılmadan direk if bloğunun sonuna atlanıyor. Yani bu şekilde bir adet if ve n adet else if satırı içeren bir if bloğunda en fazla bir kod bloğu çalışabilir. Koşullar sıra ile taranırken bir tanesi doğru olduğu anda, ona karşılık gelen kod bloğu çalıştırılır ve son else if'e karşılık gelen kod bloğundan bir sonra gelen komut ile devam edilir. Eğer koşulların hepsi yanlış ise kod bloklarından hiçbiri çalıştırılmaz.

Son olarak if komutunu görebileceğimiz bir format da şu:

if ( kosul1 )
{
  komut;
  komut;
  ...
}
else if ( kosul2 )
{
  komut;
  komut;
  ...
}
else
{
  komut;
  komut;
  ...
}

Bu daha önce bahsettiğimiz iki formun birleşimi. Burada tahmin edebileceğiniz gibi önce koşul1 ardından koşul2 kontrol ediliyor. Bu ikisinden biri doğru ise ona karşılık gelen kod bloğu çalıştırılıyor. Eğer iki koşul da yanlış ise else bloğu çalıştırılıyor. Dikkat ederseniz if komutu bu şekilde kullanıldığı zaman içerdiği kod bloklarından biri mutlaka ama mutlaka çalışıyor.

Nitekim, yukarıdaki text adventure örneğimizde if komutunu bu şekilde kullandık. Şimdi if komutunun çalışmasında bu kadar kritik rol oynayan koşulların nasıl ifade edildiğine bir bakalım.

4.1. Koşullu İfadeler

Programımızda herhangi bir anda içinde bulunulan durumu anlamak için koşullu ifadeleri kullanırız. Koşullu ifadeler genelde şöyle şeylerdir:

  1. x değişkeninin değeri 5 mi?

  2. k değişkeninin değeri 10'dan küçük mü?

  3. ...

Bu ifadelerden en sık kullandığımız kuşkusuz eşitlik kontrolüdür. Bunu text adventure örneğimizde de kullandık.

komut == 1

Burada çok dikkat etmeniz gereken bir nokta eşitlik kontolü için bir değil iki kere = karakterinin kullanılması. Yani = değil == kullanıyoruz. En sık kullanılan karşılaştırma operatörlerine bakacak olursak:

  1. a > b : a, b'den büyük mü?

  2. a >= b : a, b'den büyük veya eşit mi?

  3. a < b : a, b'den büyük mü?

  4. a <= b : a, b'den büyük veya eşit mi?

  5. a == b : a, b'ye eşit mi?

  6. a != b : a, b'den farklı mı?

4.2. Yeni bir tip: bool

Bu noktada hemen önemli bir standart tipi daha tanıtıyoruz. int ve std::string'den sonra üçüncü öğrendiğimiz tip bool. Bool tipinden tanımlanan değişkenler içlerinde 'doğru' veya 'yanlış' olmak üzere iki değerden birini taşıyabilirler. Başka bir deyişle, bool tipinden bir değişkenin değeri ya 'doğru' ya 'yanlış'tır. Tabii programlarımızda bu iki değerin ingilizce karşılıkları olan 'true' ve 'false' ifadelerini kullanıyoruz. Örneğin

bool programSonu;
programSonu = false;
...
programSonu = true;
...
if ( programSonu )
{
  std::cout << “Gule gule”
}

Bu teorik örnekte önce programSonu adında ve bool tipinde bir değişken tanımlıyoruz. Ardından bu değişkene hemen false değeri veriyoruz. Daha sonra programımızın ilerilerinde bir yerde bu değişkeni true yapıyoruz. Dikkat ederseniz if komutunda koşul olarak yukarıda gördüğümüz koşullu ifadelerden biri yerine direk olarak bool tipindeki değişkeni kullanabiliyoruz.

Bunun sebebi aslında koşullu ifadelerde geçen < veya == gibi gösterimlerin de aslında birer operatör oluşu. Az önce operatörlerin nasıl bir veya iki argüman alıp bunlar üzerinde bir işlem yapıp sonuç olarak bir deger döndürdüğünden bahsetmiştik. İşte < ve == gibi karşılaştırma operatörleri de karşılaştırdıkları iki değeri argüman olarak alır, vaadettikleri karşılaştırmayı yapar ve sonucu bool tipinden geçici bir değişken olarak döndürürler. Yani aslında if komutunun gerçekte beklediği şey bool tipinde bir değerdir. Karşılaştırma operatörlerinin bütün yaptığı argümanları arasında istenen karşılaştırmayı yapıp sonucu bool olarak döndürmektir.

if ( boolTipindeBirDeğer )

Biliyorum bu noktada pekçok yeni şey öğrendiniz ve yoruldunuz. Ancak artık daha hızlanabileceğimiz bir noktadayız. İsterseniz bir ara verip geri gelin.

5. Döngülere başlangıç: while

C++'da döngüler bir kod bloğunun, bir takım koşullara bağlı olarak tekrar tekrar çalıştırılması ile elde edilir. İlk olarak while komutuna bakalım

while ( kosul )
{
  komut;
  komut;
  ...
}

Burada kolaylıkla anlayacağınızı tahmin ettiğim şekilde, while komutu sayesinde koşul doğru olduğu müddetçe kod bloğu tekrar tekrar çalıştırılacak. Burada değinmemiz gereken şey koşulun hangi zamanlarda kontrol edildiği. While komutuna programımızın akışı ilk geldiğinde koşul kontrol edilir. Bu noktada koşul yanlış ise kod bloğu komple atlanarak bir sonraki komuttan devam edilir. Eğer koşul doğru ise kod bloğu çalıştırılır. Kod bloğundaki son komut da işletildikten sonra, koşul yine kontrol edilir. Bu esnada eğer koşul hala doğru ise tekrar kod bloğunun ilk komutundan başlanarak kod bloğu bir kez daha çalıştırılır. Bu şekilde kod bloğunun her sonuna gelişimizde koşul tekrar kontrol edilir ve doğru kaldığı sürece kod bloğu tekrar tekrar çalıştırılır. Aşağıdaki iki örneğe bakınca koşulun kontrol edilmesi anının neden önemli olduğunu daha iyi anlayacaksınız.

int i = 0;
while ( i != 5 )
{
    komut1;
    i++;
    komut2;
}

Bu örnekte önce i adlı tamsayı değişkenimiz tanımlanıp sıfıra eşitleniyor. Ardından while döngüsüne giriyoruz. İlk önce koşula bakılıyor. i değişkeninin değeri şu an beş değil. Bu yüzden koşul doğru. Öyleyse döngünün içindeki komutlar çalışmaya başlıyor. Komut1 çalıştıktan sonra i++ komutu ile değişkendeki değeri bir artırıyoruz. Ardından komut2 çalışıyor. Sonra tekrar koşul kontrol ediliyor. i hala beşe eşit değil ve koşul doğru. Tekrar döngüye giriyoruz. Bu döngüde 5 kere döndükten sonra en sonunda i değişkeninin değeri 5'e eşit oluyor ve i != 5 koşulu artık yanlış hale geliyor. Bu noktada döngüden çıkılıyor ve sonraki komuttan devam ediliyor.

Burada dikkat edeceğiniz nokta hem komut1 hem de komut2 nin beşer kere çalışması. Bu bazı okuyucuları şaşırtabilir. Sonuçta i'nin değerini 5'e artıran 5. i++ komutunda neden donguden çıkılmadığını merak edebilirsiniz. Bunun sebebi döngüden çıkışa sebep olacak olan kontrolün ancak döngü sonunda yapılması. Peki eğer bir döngüdeki komutların ortasında döngüden çıkmak istersek ne yapıyoruz?

Break komutu

Break komutu döngülerin içinde kullanılan ve döngünün herhangi bir yerinde iken kalan komutları bırakıp döngü dışına çıkılmasını sağlayan komuttur. Aşağıdaki örneğe bir bakın:

while ( true )
{
    komut1;
    komut2;
    if ( kosul )
    {
        break;
    }
    komut3;
}

Bu örnekte bir sonsuz döngü örneği görüyoruz. While ( true ) ifadesinde koşul olarak true sabiti kullanıldığı ve bu sabit değişemeyeceği için bu döngünün içine break komutu koymazsak bu döngüden sonsuza kadar çıkamayız.

Bu kullanım özellikle döngüden çıkmak için oluşması gereken koşul döngüdeki kod bloğunun ortalarında bir yerde oluşabiliyorsa faydalı. Mesela yukarıdaki örnekte koşul belki de ancak komut2’den sonra kontrol edilmeli. O durumlarda break çok faydalı.

6. Sonuç

Geniş kapsamlı bir bölümü geride bıraktık. Önümüzdeki sayıda kaldığımız yerden devam edeceğiz.

plazma - (2006 - 2011)