compound literals (bileşik sabitler)

C99 standartları ile C diline eklenen en önemli araçlardan biri “compound literal”. Türkçe karşılığı olarak “bileşik sabit” teriminin kullanılmasını öneriyorum.
C’de yazdığımız kodlarda sıklıkla şöyle bir durumla karşılaşıyoruz: Bir diziye, bir yapı (structure) ya da bir birlik (union) nesnesine ihtiyacımız var. Ancak söz konusu nesneyi muhtemelen yalnızca tek bir yerde kullanacağız. Bu durumda isimlendirilmiş bir değişken tanımlamamız gerekiyor. Aşağıdaki koda bakalım:

main işlevi içinde tanımlanan rec ve a nesnelerinin yalnızca draw_rect ve foo işlevlerine yapılan çağrılarda kullanılmak için tanımlandığını düşünelim. Kod bu niyeti açıkça anlatmadığı için okuyucuyu da yanıltıyor. Ben böyle bir kodu okuduğumda bu nesnelerin kapsamları (scope) içinde tekrar kullanılacaklarını düşünüyorum. Bu tür kodlarda bir başka sorun da “kapsam sızıntısı” (scope leakage) yani bu isimlerin kapsamlarının gereksiz yere geniş tutulması. Bu nesneler bir daha kullanılmayacak olsalar da kapsamları içinde yanlışlıkla isimlerinin yazılması bir kodlama hatasına neden olabilir.
Bir “compound literal” kullanarak isimlendirilmemiş bir dizi, yapı ya da birlik nesnesi oluşturabiliriz:

İşlevlere gönderdiğimiz argümanlar bileşik sabitler. Aslında struct Rect türünden bir yapı nesnesini ve 5 öğeli bir int diziyi isim vermeden oluşturmuş olduk. Şimdi “compound literal” ifadelerine yönelik sentaksı ayrıntılarıyla ele almaya başlayabiliriz. Önce dizi oluşturan bileşik sabit ifadelerini inceleyelim: Sentaksta yer alması gereken parantez içine dönüşüm türünü (cast type) yazıyoruz. Bu oluşturulacak dizinin türü. Daha sonra küme parantezi içinde oluşturacağımız dizinin öğelerine verdiğimiz ilk değerleri listeliyoruz. Örneğin

ifadesi ile 3 öğeli bir int dizi oluşturmuş oluyoruz. Parantez içinde dizinin türünü belirtirken dizinin boyutunu yazabileceğimiz gibi boyut değerinin çıkarımını derleyiciye de bırakabiliyoruz:

Yukarıdaki ifade ile 6 öğeli int bir dizi oluşturduk. Dizinin öğe sayısını belirtir ve öğe sayısından daha az sayıda ilk değer sağlarsak dizinin kalan öğeleri varsayılan değerlerle (tamsayı ve gerçek sayı dizileri için 0, pointer dizileri için NULL pointer) hayata başlıyorlar:

ifadesi ile 20 öğeli bir dizi oluşturarak dizinin ilk 3 öğesinin alacağı değerleri belirtmiş olduk. Dizinin kalan 17 öğesi 0. değerleriyle hayata başlayacak. Dizi öğelerine ilk değer verirken yine C99 standartlarıyla dile eklenen “designated initializer” denilen sentaks ile dizinin seçilmiş öğelerine ilk değer verip diğer öğelerini varsayılan değerlerle başlatabiliyoruz:

Yukarıdaki ifade ile 100 öğeli bir dizi oluşturduk. dizinin sırasıyla 12, 34 ve 67 indisli öğelerine ilk değerlerini verdik ve kalan öğelerinin hayata 0 değerleriyle gelmesini sağladık. “designated initializer” kullanılması durumunda yine dizinin boyutunu belirtmek zorunda değiliz:

Yukarıdaki ifade ile bu kez 68 öğeli bir dizi oluşturmuş olduk. Yapı ya da birlik nesnelerinin “compound literal” biçiminde oluşturulması için kullanılması gereken sentaks da neredeyse aynı. Employee isimli bir yapı türünün bildirildiğini düşünelim:

Şimdi bu türden nesnelerin oluşturulmasını sağlayacak bazı “compound literal” ifadeleri yazalım:

Yukarıdaki ifade ile Employee türünden bir nesneyi tüm öğelerine ilk değer vererek oluşturduk.

Yukarıdaki ifadede ise oluşturduğumuz Employee nesnesinin yalnızca name isimli öğesine ilk değer verdik. Nesnemizin diğer öğeleri varsayılan değerlerle hayata başlamış oldu.

Yukarıdaki ifade de ise oluşturduğumuz Employee nesnesinin seçilmiş öğelerine “designated initializer” sentaksı ile ilk değer verdik. Şimdi de aşağıdaki kodu inceleyelim:

Yukarıdaki kodda main işlevi içinde öğeleri Student türünden boyutu 10 olan bir dizi tanımlanıyor ve dizinin belirlenmiş öğelerine ilk değer veriliyor. Daha sonra dizinin 1 ve 2 indisli öğelerine Student türünden bileşik sabit ifadeleri ile atamalar yapılıyor. Aynı türden yapı nesnelerinin birbirlerine atanabildiğini hatırlayalım.

Compound literal ifadeleri ile oluşturulan nesnelere sabit ifadeleri (constant expressions) ile ilk değer verme zorunluluğu yok:

Yukarıda tanımlanan func işlevi içinde oluşturulan 3 öğeli int diziye isimlendirilmemiş nesneye işlevin parametre değişkenleri ile ilk değer veriliyor. Diziden adrese dönüşüm (array to pointer conversion) kuralı burada da geçerli.

Compound literal ifadeleri ile tekil (scalar) türlerden de nesneler oluşturmamız mümkün olsa da uygulamalarda böyle bir gereksinim olduğunu düşünmüyorum:

Yukarıdaki örneği yalnızca kodun geçerli olduğunu göstermek için verdim.

“literal” sözcüğü “sabit” anlamında kullanılsa da “compound literals” biçiminde oluşturulan nesnelerin değerlerini değiştirmek tanımlı (defined) davranış niteliğinde:

Yukarıdaki örnekte oluşturulan Employee nesnesinin adresi ile p isimli pointer değişkene ilk değer veriliyor. Daha sonraki deyimlerle nesnemizin name ve wage isimli öğelerinin değerlerinin değiştirildiğini görüyorsunuz. Bileşik sabit ifadeleri ile oluşturduğumuz dizileri de değiştirebiliriz:

Bu şekilde oluşturulan char türden dizilerde tutulan yazıları da değiştirebiliriz:

Ancak bir compound literal ifadesi ile const bir nesne de oluşturmamız mümkün:

Global kod alanında oluşturulan “compound literal” nesneleri, diğer isimlendirilmiş global nesneler gibi statik ömür (static storage class) kategorisindeler. Blok içinde oluşturulan nesneler ise otomatik ömre (automatic storage class) sahipler:

Yukarıda tanımlanan func işlevi içinde oluşturulan içsel blokta oluşturulan int türden otomatik ömürlü nesnemizin adresini p isimli bir pointer değişkene atıyoruz. Otomatik ömürlü nesnenin hayatı oluşturulduğu kapsamı sonlandıran “}” atomunun bulunduğu yerde sona erecek. Bloğun dışındaki kodlar yürütüldüğünde artık nesnemiz hayatta olmadığı için p pointer değişkeni bu durumda geçersiz (dangling) durumda. Şimdi de aşağıdaki koda bakalım:

Yukarıdaki kodda drawline işlevinin tanımında yer alan for döngüsünün her turunda yeni bir Point nesnesi oluşturuluyor. Böylece işlev (0,0) ve (9, 9) noktalarını birleştiren bir doğru çiziyor.
Compound literal ifadeleri sabit ifadesi kategorisinde olmadıkları için normal olarak statik ömürlü bir nesneye bir compound literal ifadesi ile ilk değer vermemiz geçerli değil:

Yukarıdaki kodda foo içinde yapılan tanımlamaların hiçbiri geçerli değil. Ancak bir GNU eklentisiyle GCC derleyicisinde bu mümkün kılınmış. Bu eklenti kullanıldığında yukarıdaki tanımlamalar
aşağıdaki gibi bir kodla aynı anlama geliyor:

Bileşik sabit ifadeleri C++ dilinin sentaksında yer almıyor. Ancak başta GCC olmak üzere birçok C++ derleyicisi bu özelliği bir eklenti (extension) olarak kullanıma sunuyor.

Share

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Kod Eklemek İçin Okuyun