Basit bir C bilmecesi

taydin

Timur Aydın
Staff member
Katılım
24 Şubat 2018
Mesajlar
21,539
Aşağıda iki tane basit C programı var. Bunları çalıştırdığımızda ekrana ne yazılır? Programlar doğru çalışıyor mu? Çalışıyor diyorsanız, neden? Çalışmıyor diyorsanız, gene nedenini açıklayınız.

Programları derlemeden önce, sadece bakarak soruları cevaplamaya çalışın. Cevap bulamazsanız, derleyin, sonucu görün, ve sonra da sonucu yorumlayın.

Kod:
#include <stdio.h>

int buffer[5] = {1, 2, 3, 4, 5};

int main()
{
   int toplam = 0;
   unsigned int i;

   for (i = 0; i <= 5; i++)
   {
      toplam += buffer[i];
   }

   printf("toplam = %d\n", toplam);
}

Kod:
#include <stdio.h>

int main()
{
   int buffer[5] = {1, 2, 3, 4, 5};
   int toplam = 0;
   unsigned int i;

   for (i = 0; i <= 5; i++)
   {
      toplam += buffer[i];
   }

   printf("toplam = %d\n", toplam);
}
 
Son düzenleme:
İki programı derledim. İlk program doğru çalışıyor ama ikinci program 30 binli bir toplam veriyor. Bu toplam da her çalıştırmada değişiyor. Çok tuhaf, iki program için de buffer içeriği aynı, nasıl farklı bir sonuç çıkabilir anlamadım. İkincisinin de doğru çalışması lazım.
 
Programları yeterince dikkatli incelememişsin. İki program da sorunlu. Her iki programı da debugger altında çalıştır, adım adım. Her adımda gerçekleşen şeyin doğru olup olmadığından emin ol.
 
İlk program doğru sonucu buluyor, 5 tane sayı topluyor ve sonucu ekrana yazıyor. Debugger ile de çalıştırmayı deneyeceğim.
 
Bir program belli bir durumda doğru sonucu yazıyorsa, hemen bu programın doğru olduğunu söyleyemeyiz. Eğer bir program, hesaplama kapasitesi dahilinde olan bütün değerler için doğru sonucu veriyorsa ancak o programa "doğru çalışıyor" diyebiliriz. Şu örneği düşün:

"Verilen sayının karekökünü bulan bir program yaz" dedik. Ama programcı, verilen sayının karekökünü alacağına sayıyı 3'e bölmüş. Bu durumda sayı olarak 9 verirsek, program bize 3 cevabını verecek ve bu doğru cevap. Ama program doğru mu? Değil. 36'nın karekökünü hesaplattırsak, bize 12 diyecek :)

İki program da sorunlu. Debugger ile adım adım çalıştırıp her adımın doğru olduğunu teyit etmen lazım.
 
Tamam şimdi gördüm sorunu. İlk bakışta farkedilmiyor ama her iki döngü de 5 defa değil 6 defa çalışıyor. Dikkatli bakmayınca for loop'taki <= ifadesi gözden kaçıyor. 6 defa toplama yapıyor, ilk programda 6'ncı sayı 0 olduğu için sonucu etkilemiyor, ama ikinci programda 6'ncı sayı her seferinde değişen bir değere sahip. O yüzden her seferinde farklı bir sonuç çıkıyor!
 
Evet aynen problem o. Döngü, buffer'ın sınırlarını aşıyor. 0'dan 4'e kadar değerler için dönülmesi gerekirken, 0'dan 5'e kadar değerler için dönüyor. buffer[5] artık buffer'ın dışında kalıyor.

C ve C++'da global olarak tanımlanan değişkenler ve buffer'lar, derleyici tarafından 0 ile doldurulur. Böylece program çalıştığında, oradaki değerleri ilk başta hep 0 olarak bulur. O yüzden tesadüfen program doğru çalışıyor. Ama buffer global değil de, main fonksiyonunun içinde local (stack üzerinde) bir buffer olunca, iş değişiyor. Stack üzerindeki bir değişken veya buffer'a programın kendisi değer ataması yapmazsa, orada tamamen rastgele bir değer olacaktır.

İlk program tesadüfen doğru çalışıyor, ama örneğin programımız o global buffer'dan okuma yerine o buffer'a yazma yapsaydı, o zaman buffer'dan öteye birşeyler yazacaktı, ve o buffer'dan sonra tanımlanan başka global değişkenler olsaydı, onların içeriğini bozacaktı. Bu tip buffer hataları C/C++ da çok yaygındır, o yüzden buffer işlemlerinde sınırları aşmamak için çok dikkat etmeliyiz.
 
Yeni GCC ve LLVM ile buffer sınırlarını aşan program yapılarını tespit etme olanağı var. Olabilecek her durumu algılayabiliyor mu bilmiyorum, ama bu tip bariz problemleri algılayabiliyor. GCC'nin -fsanitize=address opsiyonuna bakınız.

Bu opsiyon sadece buffer sınır aşma problemleri değil, memory leak ve race condition denen problemlerde de faylalı olduğu belirtiliyor. Ama bu özellikleri şimdiye kadar kullanmadım.
 
Bahsettiğiniz kontrolleri yapan bir de "valgrind" denen bir kütüphane var. Arada bir valgrind'i kullanıyorum, ama hep şöyle bir sıkıntı ile karşılaşıyorum: Üzerinde uğraştığım projelerde, bizim geliştirmediğimiz başka birçok kütüphane kullanmak durumundayız. Bu kütüphanelerin geliştirmesini yapan kişiler de valgrind kullanma konusunda çok hevesli olmadıkları anlaşılıyor, çünkü program çalıştırıldığında yüzlerce valgrind uyarı mesajı ile karşılaşılıyor. Bu uyarı mesajı seli içerisinde hangi mesajların doğrudan sizin yazdığınız program ile ilgili olduğunu hemen görmek mümkün değil, çok uğraştırıyor. Eğer herkeste valgrind kullanma disiplini olsa, veya -fsanitize kullanma disiplini olsa, o zaman hakikaten bu araçların olması bulunmaz nimet.

Ama bu sorun olsa bile, valgrind ve dediğiniz gibi -fsanitize'i arada bir yazdığımız program üzerinde kullanmalıyız ve verilen uyarılar doğrultusunda programı gözden geçirmeliyiz.
 
Aslında valgrind ve @theroot un bahsettiği -fsanitize ile ilgili ayrı ayrı konular açıp bunlarla ilgili örnek senaryolar yapmak lazım. Bunu not alıyorum.
 

Çevrimiçi personel

Forum istatistikleri

Konular
5,653
Mesajlar
97,274
Üyeler
2,438
Son üye
İbrahimSönmez

Son kaynaklar

Son profil mesajları

cemalettin keçeci wrote on HaydarBaris's profile.
barış kardeşim bende bu sene akıllı denizaltı projesine girdim ve sensörleri arastırıyorum tam olarak hangi sensör ve markaları kullandınız yardımcı olabilir misin?
m.white wrote on Altair's profile.
İyi akşamlar.Arabanız ne marka ve sorunu nedir.Ben araba tamircisi değilim ama tamirden anlarım.
* En mühim ve feyizli vazifelerimiz millî eğitim işleridir. Millî eğitim işlerinde mutlaka muzaffer olmak lâzımdır. Bir milletin hakikî kurtuluşu ancak bu suretle olur. (1922)
Kesici/Spindle hızı hesaplamak için SpreadSheet UDF'leri kullanın, hesap makinesi çok eski kalan bir yöntem :)
Dr. Bülent Başaran,
Elektrik ve Elektronik Mühendisi
Yonga Tasarım Özdevinimcisi
Üç güzel "çocuk" babası
Ortahisar/Ürgüp/Konya/Ankara/Pittsburgh/San Francisco/Atlanta/Alaçatı/Taşucu...

Back
Top