string sabitleri (string literals)

C dilinde çift tırnak içinde yazılan karakterlerin oluşturduğu atomlara (token) string sabiti (string literal) denir. Örneğin:

ifadelerinin hepsi string sabitleridir.
C’de bir string sabiti, derleyiciler tarafından aslında char türden bir dizi (array) olarak ele alınır ve bir ifade içinde kullanıldığında söz konusu dizinin adresine dönüştürülür. C derleyicileri, derleme aşamasında bir string sabiti ile karşılaştığında, bu stringi oluşturan karakterleri belleğin güvenli bir bölgesine, sonunda sonlandırıcı karakter (null character) olacak şekilde yerleştirecek bir kod üretirler. Bu durumda string sabitleri kod içinde kullanıldıklarında aslında, derleyici tarafından oluşturulan dizilerin başlangıç adresleri olarak ele alınırlar. Örneğin:

gibi bir kod parçasının derlenmesi sırasında, derleyici önce “CAN” yazısını taşıyacak 4 elemanlı bir char dizi için belleğin güvenli bir bölgesinde yer ayırır ve bu diziye CAN yazısı ile ilk değer verir. Daha sonra bu dizinin adresiyle p gösterici değişkenine ilk değer verecek şekilde kod üretir.
Bir string sabiti kod içinde char türden bir dizinin başlangıç adresi olarak ele alındığına göre, bir string sabitinin char türden bir adres gereken yerde kullanılması geçerlidir. Aşağıdaki main işlevini derleyerek oluşturacağınız programı çalıştırın:

Yukarıdaki kodda main işlevi içinde yapılan ilk printf çağrısında “Gurbuz” string sabiti ile %p format dönüştürme karakterleri eşleniyor. printf işlevi ile, adres bilgilerinin sayısal bileşenlerinin %p format dönüştürme karakteriyle ekrana onaltılık sayı sisteminde yazdırılabileceğini hatırlayın. Bu durumda çalışan kod, derleyicinin “Gurbuz” yazısının yerleştirdiği dizinin başlangıç adresini ekrana yazar. İkinci printf çağrısında ise “Gurbuz” yazısı %s format dönüştürme karakteri ile eşleniyor. Bu durumda ekrana ilgili adresteki yazı, yani

yazısı yazdırılır. Şimdi de aşağıdaki çağrıya bakın:

“Kagan” string sabitinin bu kez içerik işlecinin terimi olduğunu görüyorsunuz. İçerik işleci, terimi olan adresteki nesneye erişimi sağladığına göre, bu nesnenin değeri ‘K karakterinin kod numarasıdır. Çağrılan putchar işlevinin çalışmasıyla standart çıkış akımına K karakteri verilir.
Aşağıda tanımlanan getHexChar işlevi, onaltılık sayı sisteminde bir basamak değeri hangi karakter ile gösteriliyorsa, o karakterin kullanılan karakter setindeki kod numarasını döndürüyor:

İşlevin geri dönüş değeri

ifadesinin değeridir. Bu da string sabitine ilişkin yazının yerleştirildiği dizinin val indisli karakterinin değeridir. char türden bu nesnenin değeri de yazıda yer alan karakterlerden herhangi birinin kod numarasıdır. main işlevinde 0 – 15 aralığındaki tamsayı değerleri döngü içinde getHexChar işlevine gönderilerek işlevden geri dönüş değeri ile alınan karakterler putchar işlevi ile standart çıkış akımına gönderiliyor. Programın ekran çıktısı aşağıdaki gibi olur:

String sabitlerine karşılık gelen dizilerin bellekte kaplayacakları yer (storage) derleme zamanında derleyici tarafından belirlenir. Aşağıdaki program her çalıştırıldığında ekrana hep aynı adres değeri yazılır:

string sabitleri salt okunur yazılardır

String sabitleri salt okunur bellek alanlarında tutulabilir. C dilinin standartlarına göre bir string sabitinde yer alan karakterlerin kaynak kod içinde değiştirilme girişimi tanımsız davranıştır (undefined behavior). Aşağıdaki örneği inceleyin:

main işlevi içinde tanımlanan ptr isimli gösterici değişken “Durak” yazısını gösteriyor. Yazılan kod C dilinin sentaksına uygun olmasına karşın, ptr gösterici değişkeninin gösterdiği yazının değiştirilmesi tanımsız davranıştır. Bazı derleyiciler seçeneğe bağlı olarak (compiler switches) string sabitlerinin değiştirilmesine olanak veren kod üretebilirler. Programcıya derleyiciler tarafından sunulan bu seçenek geçmişe doğru uyumluluğun korunması gibi çok özel durumlarda kullanılmalıdır.

string sabitlerinin işlevlere argüman olarak gönderilmesi

Parametre değişkeni char türden bir gösterici olan (char *) bir işlevi, char türden bir adres ile çağırmak gerektiğini biliyorsunuz. Çünkü char türden bir gösterici değişkene, doğal olarak char türden bir adres atanmalıdır. Derleyiciler açısından string sabitleri de char türden bir dizi olduklarına göre, parametre değişkeni char türden gösterici olan bir işlevi, bir string sabiti ile çağırmak son derece doğal bir durumdur:

Burada derleyici “Kagan Aslan” yazısını belleğe yerleştirip sonuna sonlandırıcı karakteri koyduktan sonra artık bu string sabitini, karakterlerini yerleştirdiği bellek bloğunun başlangıç adresi olarak görür. puts işlevinin parametre değişkenine de artık char türden bir adres kopyalanır. puts işlevi parametre değişkeninde tutulan adresten başlayarak sonlandırıcı karakteri görene kadar tüm karakterleri ekrana yazar. Bu durumda ekranda

yazısı çıkar.

Yukarıdaki kodda “Oguz Karan” yazısı str adresine kopyalanır. String sabiti ifadelerinin derleyici tarafından char türden bir dizinin adresi olarak ele alındığı düşünülmelidir.

C dilinde bir yazı üstünde işlem yapacak bir işlevin parametre değişkeninin char * olarak bildirilmesinin, bu işlevin adresini aldığı diziye bir yazma işlemi yapacağını ya da adresini aldığı yazıyı değiştireceği anlamına geldiğini hatırlayalım. Eğer işlevin parametre değişkeni const char * olarak (low level const) bildirilmiş ise işlev adresini aldığı yazıyı yalnızca okuma amacıyla kullanacak demektir. Bir string sabiti salt okunur bir yazıyı ifade ettiğinden yalnızca parametresi const char * türden olan işlevlere gönderilmelidir. Eğer bir string sabiti yazma amaçlı bir işleve argüman olarak gönderilirse tanımsız davranış oluşur:

Yukarıdaki kodda p isimli gösterici değişkene bir string sabiti ile ilk değer veriliyor. Daha sonra p değişkeninin değeri olan adres, bir POSIX işlevi olan strrev’e argüman olarak gönderiliyor. strrev işlevinin bildirimi şöyle:

strrev adresini aldığı yazıyı ters çeviriyor. Bu durumda strrev işlevi, değiştirilmemesi gereken yani salt okuma amacıyla kullanılması gereken “mustafa” yazısını değiştirme girişiminde bulunacak ve çalışma zamanı hatası oluşacak.

özdeş string sabitleri

C derleyicileri kaynak kodun çeşitli yerlerinde tamamen özdeş string sabitlerine rastlasa bile bunlar için farklı yerler ayırabilir. Ya da derleyici, string sabitlerinin salt okunur yazılar olmasına dayanarak, özdeş string sabitlerinin yalnızca bir kopyasını bellekte saklayabilir. Özdeş string sabitlerine ilişkin yazıların bellekte nasıl saklanacağı derleyicinin seçimine bırakılmıştır. Birçok derleyici, özdeş string sabitlerinin bellekte nasıl tutulacakları konusunda programcının seçim yapmasına olanak verir.

string sabitlerinin karşılaştırılması

string sabitlerinin doğrudan karşılaştırılması yanlış bir işlemdir:

Yukarıdaki kodun çalıştırılması durumunda, ekrana “yanlış” ya da “doğru” yazdırılması garanti altında değildir. Üretilecek kod derleyiciye bağlı olarak değişebilir. Eğer derleyici iki “Ankara” yazısını bellekte ayrı ayrı yerlere yerleştirmiş ise, == karşılaştırma işleci 0 (yanlış) değerini üretir. Ancak bir derleyici, “Ankara” yazısını tek bir yere yerleştirip her iki string sabitini de aynı adres olarak da ele alabilir. Böyle bir durumda == karşılaştırma işleci 1 (doğru) değerini üretir.
Benzer bir yanlışlık aşağıdaki kod parçasında da yapılıyor:

Yukarıdaki programda str bir dizinin ismi. str ismi işleme sokulduğunda derleyici tarafından otomatik olarak bu dizinin başlangıç adresine dönüştürülür. pstr ise char türden bir gösterici değişkendir. pstr gösterici değişkenine “bukalemun” string sabiti ile ilk değer verildiğinde, derleyici önce “bukalemun” yazısını bellekte güvenli bir yere yerleştirir. Daha sonra pstr değişkenini yazının yerleştirildiği yerin başlangıç adresi ile hayata başlatırKullanıcının, parola olarak “bukalemun” girişi yaptığını varsayın. Bu durumda if deyimi içinde yalnızca s adresiyle pstr değişkeninin değeri olan adresin eşit olup olmadığı sınanır. Bu adresler eşit olmadıkları için ekrana “yanlış parola” yazılır. İki yazının birbirine eşit olup olmadığı standart strcmp işlevi ile sınanmalıydı:

Tabi eşitlik ya da eşitsizlik karşılaştırması gibi, büyüklük küçüklük karşılaştırması da doğru değildir:

Yukarıdaki if deyiminde CAN ve ATA isimleri strcmp işlevinin yaptığı biçimde, yani birer yazı olarak karşılaştırılmıyor. Aslında karşılaştırılan yalnızca iki adresin sayısal bileşenleridir.

string sabitlerinin ömürleri

String sabitleri statik ömürlü varlıklardır. String sabitleri ile ifade edilen yazılar, tıpkı global gibi programın yüklenmesiyle bellekte yer kaplamaya başlar, programın sonuna kadar bellekte kalır. Dolayısıyla string sabitleri çalıştırılabilen kodu büyütür. Birçok sistemde statik ömürlü verilerin yerleştirileceği bellek alanının büyüklüğü belli bir sınırlamaya tabidir. String sabitleri derleyici tarafından object modüle bağlayıcı program tarafından da çalıştırılabilir dosyaya yazılır. Programın belleğe yüklenmesiyle hayat kazanırlar.

bir işlevin bir string sabiti ile geri dönmesi

Adrese geri dönen bir işlevin, yerel bir değişkenin ya da yerel bir dizinin adresi ile geri dönmesi, gösterici hatasıdır. İşlev sonlandığında yerel değişkenler bellekten boşaltılacağı için, işlevin geri döndürdüğü adres güvenli bir adres olmaz. Aşağıdaki programda böyle bir hata yapılıyor:

Ancak char türden bir adrese geri dönen bir işlev bir string sabiti ile geri dönebilir. Bu durumda bir çalışma zamanı hatası söz konusu olmaz. Statik ömürlü varlıklar olan string sabitlerine ilişkin yazılar programın çalışma süresi boyunca bellekteki yerlerini korurlar. Örneğin aşağıdaki işlev geçerli ve doğrudur:

Yukarıda tanımlanan get_day_name işlevi bir günün sıra numarasını alarak ilgili günün ismini içeren bir yazının başlangıç adresini döndürüyor. İşleve gönderilen değer istenen aralıkta değil ise işlev NULL adresine geri dönüyor.

string sabitlerinin derleyici tarafından birleştirilmesi

String sabitleri tek bir atom (token) olarak ele alınır. Bir string sabiti aşağıdaki gibi parçalanamaz:

Ancak yazıların uzunlukları arttıkça, hem onların bir string sabiti olarak tek bir satırda yazılması hem de kodda bu yazıların okunması zorlaşır. Kod editörlerinde bir satırlık görüntüye sığmayan yazılar kaynak kodun okunmasını zorlaştırır. Uzun yazılara ilişkin string sabitlerinin parçalanmasına olanak vermek amacıyla, C dilinde aralarında boşluk karakteri dışında başka bir karakter olmayan string sabitleri derleyici tarafından birleştirerek tek bir string sabiti olarak ele alınır. Örneğin:

geçerli bir ifadedir. Bu durumda derleyici iki string sabitini birleştirirek kodu aşağıdaki biçimde ele alır:

Derleyicinin iki string sabitini birleştirmesi için, string sabitlerinin arasında boşluk karakterlerinin (white space) dışında hiçbir karakterin olmaması gerekir:

bildirimi ile

bildirimi eşdeğerdir.

String sabiti kapatılmadan bir ters bölü karakteri ile sonlandırılarak da sonraki satıra geçiş sağlanabilir. Örneğin:

bildirimi ile

bildirimi eşdeğerdir. Ters bölü karakterinden sonra string sabitinin karakterleri aşağıdaki satırın başından devam etmelidir. Söz konusu string sabiti aşağıdaki gibi yazılırsa:

satır başındaki boşluk karakterleri de yazıya katılır. Sonuç aşağıdaki deyime eşdeğer olur:

string sabitlerinde ters bölü karakter sabitlerinin kullanılması

String sabitlerinde ters bölü karakter sabitleri de (escape sequence) kullanılabilir. Derleyiciler string sabitleri içinde bir ters bölü karakteri gördüğünde, onu yanındaki karakter ile birlikte tek bir karakter olarak ele alır. Örneğin:

Yukarıdaki kodda string sabiti içinde kullanılan \t tek bir karakterdir (9 numaralı ASCII karakteri olan tab karakteri).
Yani

deyimi ile ekrana

yazdırılır.
String sabitlerinde doğrudan çift tırnak ya da ters bölü karakterleri kullanılamaz. Bu karakterlerin özel işlevleri vardır. Bir string sabiti içinde “çift tırnak” karakter sabitinin kendisini ifade etmek için çift tırnak karakterinden önce bir ters bölü karakteri kullanılır. Örneğin:

main işlevi içinde yapılan çağrı ile ekrana

yazısı yazdırılır. Aşağıdaki koddaki hatayı görebiliyor musunuz?

String sabiti içinde yer alan ters bölü karakter sabitleri, onaltılık sayı sisteminde de ifade edilebilir. Aşağıdaki programı derleyerek çalıştırın:

String sabiti içinde yer alan ters bölü karakter değişmezleri sekizlik sayı sisteminde de ifade edilebilir. Aşağıdaki programı derleyerek çalıştırın:

boş string sabiti

Bir string sabiti bir boş yazıyı yani uzunluğu sıfır olan bir yazıyı ifade edebilir (null string). Bu durum iki çift tırnak karakterinin arasına hiçbir karakter yazılmaması ile oluşturulur:

Yukarıdaki bildirim ile ptr gösterici değişkenine boş yazı ile ilk değer veriliyor. Boş string sabitleri de bellekte bir adres belirtir. Derleyici boş bir string sabiti gördüğünde, bellekte güvenilir bir yere yalnızca sonlandırıcı karakteri yerleştirir.
Boş bir yazı uzunluğu sıfır olan bir yazıdır. Aşağıdaki programı derleyerek çalıştırın:

gösterici değişkenlere string sabitlerine ilk değer verilmesi

char türden gösterici değişkenlere string sabitleri ile ilk değer verilebilir. Örneğin:

String sabitleri derleyiciler tarafından char türden bir dizi adresi olarak ele alındığına göre, char türden gösterici değişkenlere string sabitleri ile ilk değer verilmesi doğal bir durumdur.
Dizilere çift tırnak içinde ilk değer verilmesiyle gösterici değişkenlere çift tırnak içinde ilk değer verilmesi tamamen farklıdır.
Gösterici değişkenlere string sabitleri ile ilk değer verildiğinde, derleyici string sabitini bir adres ifadesi olarak ele alır. Yani ilgili yazı belleğe yerleştirildikten sonra başlangıç adresi gösterici değişkene atanır. Oysa dizilerde önce dizi için yer ayrılır, daha sonra karakterler tek tek dizi elemanlarına yerleştirilir. Dizilere ilkdeğer verirken kullanılan çift tırnak ifadeleri adres bilgisine dönüştürülmez. Dizi elemanlarına tek tek char türden sabitlere ilk değer verme işlemi zahmetli olduğu için, programcının işini kolaylaştırmak amacı ile böyle bir ilkdeğer verme kuralı getirilmiştir.
Bir char diziye ilk değer vermek ile, gösterici değişkenlere string sabitlerine ilk değer vermek arasındaki farka dikkat etmek gerekir:

deyimi aslında

ile aynı anlamdadır.
C’de geleneksel olarak string sabitlerinin char * türden gösterici değişkenlere atanması ya da ilk değer olarak verilmesi doğru kabul edilir. Ancak C++ dilinde bu yanlıştır ve çoğru durumda derleyicinin kontrolüne tabidir. String sabitleri salt okunur amaçlı kullanılacak yazılar olduğuna göre bunları gösterecek göstericilerin de yalnızca okuma amaçlı olarak kullanılacak, yazma işlemine izin vermeyecek göstericiler olması daha doğrudur. Kodlama hatalarına karşı string sabitlerinin char * türünden değil const char * türünden göstericilere atanması daha doğrudur.

yazı tutan char türden dizilerle bir string sabitini gösteren char türden göstericilerin karşılaştırılması

C dilinde bir yazı bilgisi en az iki ayrı biçimde saklanabilir:

1. Bir yazı char türden bir dizi içinde tutulabilir:

Yukarıdaki kodda tanımlanan foo işlevinde char türden s1 dizisine Ali Serce yazısı ile ilk değer veriliyor. Yine char türden s2 dizisine ise standart fgets işlevi ile standart giriş akımından bir yazı alınıyor. s2 dizisindeki yazı, standart strcpy işlevi ile s3 dizisine kopyalanıyor.

2. Yazı doğrudan bir string sabiti olarak kullanılır. Ya da char türden bir gösterici değişkenin bir string sabitine ilişkin yazıyı göstermesi sağlanır:

İki yöntem birbirinin tamamen eşdeğeri değildir. Aşağıdaki noktalara dikkat edilmesi gerekir:
String sabitleri olarak kullanılan yazılar statik ömürlü varlıklar oldukları için programın sonlanmasına kadar bellekte yer kaplarlar. Bir gösterici değişkenin bir string sabitine ilişkin yazıyı gösterirken, daha sonra başka bir yazıyı gösterir duruma getirilmesi, daha önceki yazının bellekten boşaltılacağı anlamına gelmez:

Statik ömürlü varlıkların saklanacağı bellek alanının kısıtlı olduğu sistemlerde, string sabitlerinin kullanımına dikkat etmek gerekir.

Yazının (const olmayan) char türden bir dizi içinde tutulması durumunda bu yazıyı değiştirmek mümkündür. Dizi öğelerine yeniden atamalar yapılarak yazı istenildiği gibi değiştirilebilir. Ama string sabitlerine ilişkin yazıların değiştirilmesi tanımsız davranış özelliği gösterir, bir hatadır.
char türden bir dizi otomatik ömürlü ya da statik ömürlü olabilir. Yani yazının bellekte kalma süresi dizinin ömrünün otomatik ya da statik ömürlü olmasına göre farklılık gösterir.

strlen işlevi ve sizeof işleci

Yazılar söz konusu olduğunda sizeof işleciyle strlen işlevinin birbiriyle karıştırılması sık karşılaşılan bir durumdur.
sizeof derleme zamanında ele alınan bir işleçtir. Yani sizeof işlecinin ürettiği size_t türünden değer derleyici tarafından derleme zamanında elde edilir. sizeof’un bir isim (identifier) olmadığını bir anahtar sözcük (keyword) olduğunu biliyorsunuz.

ifadesi size_t türündendir ve bu ifadenin değeri derleyicinin “necati” yazısını yerleştirdiği dizinin boyutu yani 7’dir.
Oysa strlen C’nin standart bir işlevidir. Şüphesiz bu işlevin size_t türünden geri dönüş değeri programın çalışması sırasında elde edilir. strlen işlevinin geri dönüş değeri, adresi alınan yazının uzunluğudur:

ifadesinin size_t türünden olan değeri “necati”, yazısının uzunluğu, yani 6‘dır.

string sabitleri nerelerde kullanılmalı

Programın çalışma zamanında dinamik olarak içeriği değişmeyen, kaynak kod içinde salt okuma amacıyla kullanılacak yazıların string sabitleriyle ifade edilmesi kaynak kodun daha kolay okunmasını ve anlaşılmasını sağlar. Aşağıdaki örneği inceleyin:

Yukarıdaki main işlevinde fileName isimli diziye standart giriş akımından bir dosya ismi alınıyor.  Eger standart giriş akımından alınıp diziye kopyalanmış bir ‘\n’ karakteri var ise bu karakterin yeri strchr işlevi ile ile bulunuyor ve bu karakterin yerine sonlandırıcı karakter yazılıyor. Böylece yazının sonundaki ‘\n’ karakteri silinmiş oluyor. Diziye alınan isim daha sonra standart strcpy işleviyle newFileName isimli diziye kopyalanıyor.
Standart strrchr işleviyle, kopyalanan yazı içinde ‘.’ karakterinin olup olmadığı, yani dosya isminin bir uzantısı olup olmadığı sınanıyor. Eğer ismin içinde nokta karakteri varsa bu karakterin adresi ptr isimli gösterici değişkende tutuluyor. Dosya isminin uzantısı yoksa, standart strcat işleviyle yazının sonuna “.dat” yazısı ekleniyor. Bu durumda artık dosya isminin uzantısı “dat” olur, değil mi? Daha sonra standart strcmp işleviyle, dosya ismi uzantısının “doc”, “gif” ya da “exe” olup olmadığı sınanıyor. Dosyanın uzantısı “doc” ise bu uzantı “txt”, “gif “ise uzantı “jpg” olarak değiştiriliyor. Eğer dosya uzantısı “exe” ise, dosyanın uzantısı siliniyor. Bunun dışındaki tüm durumlarda dosya uzantıları, “nec” yapılıyor.
Tüm bu işlemlerin yapılmasında okuma amaçlı yazılar için string sabitlerinin kullanıldığını görüyorsunuz. Bu amaçla string sabitlerinin kullanılması yerine char türden diziler kullanılsaydı programın algısal karmaşıklığı artar, okuyanlar hangi yazıların kullanıldığını kolayca göremezlerdi.

Necati Ergin

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

Bunlar da ilginizi çekebilir

string sabitleri (string literals)” için 2 yorum

  1. Necati Bey selamlar,
    son örnekte fgets enter karakterini de okuduğu için if deyimi içinde hep else düşüyor.
    ben de aşağıdaki gibi düzeltmeye çalıştım. iyi çalışmalar

    1. Merhaba Serkan Bey,
      Yazıdaki kodlarda daha önce burada gets işlevi çağrılıyordu. Daha sonra yazıyı güncellerken gets yeni standartlarla dilden kaldırıldığı için gets çağrılarını fgets çağrılarıyla değiştirdim. Ama standart giriş akımından gelecek ‘\n’ karakterini yazıdan silecek kodu eklemeyi unutmuşum. Kodu düzelttim. Teşekkür ederim.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Kod Eklemek İçin Okuyun