Kopyala & Yapıştır Hataları

Aşağıdaki kod parçacığını inceleyelim:

static int rr_cmp (uchar *a, uchar *b)
{
    if (a[0] != b[0])
        return (int) a[0] - (int) b[0];
    if (a[1] != b[1])
        return (int) a[1] - (int) b[1];
    if (a[2] != b[2])
        return (int) a[2] - (int) b[2];
    if (a[3] != b[3])
        return (int) a[3] - (int) b[3];
    if (a[4] != b[4])
        return (int) a[4] - (int) b[4];
    if (a[5] != b[5])
        return (int) a[1] - (int) b[5];  /* dikkat */
    if (a[6] != b[6])
        return (int) a[6] - (int) b[6];
    return (int) a[7] - (int) b[7];
}

Tipik bir kopyala & yapıştır hatası burada gözümüze çarpmaktadır. Programcı burada derleyici optimizasyonunu düşünmüş. Bu nedenle daha az okunabilir ama daha hızlı çalışacağını umduğu bir yöntem kullanmış. Bu daha az okunabilir olan kodu yazarken de birbirinin aynı mantıkta ilerleyen satırları kopyala & yapıştır ile tekrar etmiş.

Ancak açıklama düştüğümüz satırda görüldüğü üzere, index değerlerini artırırken a[5] olması gereken yeri güncellemeyi unutmuş ve a[1] olarak kalmış!

Bu tip hataların çok nadir olduğunu düşünmeyin, hepimiz benzerlerini vaktiyle yapmışızdır.

Yukarıdaki hatalı kod örneği, MySQL veritabanı uygulamasının kaynak kodundan alınmıştır. Bu kadar yoğun kullanılan bir uygulamada, oldukça basit olmasına rağmen zaman zaman hatalı sonuç dönülmesine yol açacak yukarıdaki hatanın kod içerisinde bulunması da oldukça güç olmuştur.

İtici olmayı göze alarak, iyi kod yazmanın altın kuralını bir kere daha yineleyelim: Derleyici için daha optimize kod yazmaya değil, programcı için daha okunabilir kod yazmaya gayret edin!

Yukarıdaki fonksiyon şu şekilde yazılmış olsaydı, muhtemelen aynı hızda çalışacak ve okuması daha kolay olduğundan hata yapma ihtimali azalacaktı:

  • Doğru kullanım:
    static int rr_cmp (uchar *a, uchar *b)
    {
      for (int i = 0;  i < 8; i++) {
          if (a[i] != b[i]) {
              return a[i] - b[i];
          }
      }
      return 0;
    }
    

Yukarıdaki kod bloğunun 4 satır daha kısaltılabileceğini görüyoruz. Tek satır yazımının nadiren işlevsel olduğu yerler vardır. Bu örnek bunlardan biri değil. Aynı kod bloğunu tek satırcılar (one liners) için aşağıya eklediğimiz versiyonuyla karşılaştırmayı deneyin. Karşılaştırmayı yaparken, bu kod parçasına proje üzerinden 4 yıl geçtikten ve o an bambaşka bir iş üzerinde çalışıyorken dönüp bakmak zorunda kaldığınızı düşünün:

  • Daha az doğru yada hatalı kullanım:
    static int rr_cmp (uchar *a, uchar *b)
    {
      for (int i = 0;  i < 8; i++) if (a[i] != b[i]) return a[i] - b[i];
      return 0;
    }