sequence point (yan etki noktası)

C programcılarının iyi bilmesi gereken konulardan biri “sequence point”. Ben bu terimi olduğu gibi dilimize çevirmek yerine kendi uydurduğum “yan etki noktası” terimini kullanıyorum. Yan etki noktasını anlamak için önce yan etki (side effect) nedir ona bakalım:
Bir ifade (expression) ya da bir işlevin (function) kendisi dışındaki bir varlığın durumunu (state) değiştirmesine “yan etki” denir. Bir başka uygun tanım da şöyle verilebilir:
Bir yan etki bir işlecin (operator), bir ifadenin (expression) bir deyimin (statement) ya da bir işlevin (function) oluşturduğu öyle bir sonuçtur ki bu sonuç söz konusu işlecin, ifadenin, deyimin ya da işlevin yürütülmesi tamamlandıktan sonra kalıcılığını sürdürür.
Örneğin bir ifadenin değerinin hesaplanmasında bir nesnenin değerinin değişmesi bir yan etkidir:

ifadesini ele alalım. Bu ifadede yer alan x değişkeninin türünün int olduğunu düşünelim. Bu ifadenin değeri 3 olarak hesaplanacaktır. (C’de atama işlecinin ürettiği değer nesneye atanan değerdir.) Bu ifadenin değerinin hesaplanması sürecine bağlı olarak x değişkeninin değeri değişecek ve 3 olacaktır.

Yine x int türden bir değişken olmak üzere

ifadesinin C dilinde hesaplanan değeri x değişkeninin bu ifadenin hesaplanmasında önceki değeridir. Ancak yan etki olarak x nesnesinin değeri 1 artacaktır.
Benzer şekilde çağrılan bir işlevin statik ömürlü bir nesnenin değerini değiştirmesi bir yan etkidir.
Bir işlevin kendisini çağıran bir koddan aldığı nesnenin değerini değiştirmesi bir yan etkidir.
volatile anahtar sözcüğü ile tanımlanan bir değişkene erişim bir yan etkidir.
Örneğin, bir dosyadan okuma ya da bir dosyaya yazma işlemi gerçekleştiren bir işlevin çağrılması bir yan etkidir.
Yan etki içeren bir işlevin çağrılması bir yan etkidir.

Yan etki ve yan etki noktası programlamaya ilişkin genel terimler olup yalnızca belirli bir programlama dili ile ilgili değildir. Yan etki noktası özellikle C dilinde büyük önem taşır. Biz şimdi yan etki noktası için C diline uygun bir tanım verelim:
Yan etki noktası zamansal öyle bir noktadır ki bu andan önce söz konusu olan tüm yan etkiler tamamlanmış (yani etkileri kalıcı hale gelmiş) ve bu noktadan sonraki yan etkiler henüz oluşmamıştır. Bir başka deyişle yan etki noktaları programın akışı içinde kaynak kodda tam nerede olduğumuzu bildiğimiz ve bundan emin olduğumuz noktalardır. İki yan etki nokta arasında kalan ifadelerin ve alt ifadelerin değerlendirilme sırası konusunda belirleyici bir durum söz konusu değildir (unspecified).

C dilinde yan etki noktaları

C dilinin standartları yan etki noktalarının söz konusu olduğu yerleri tek tek belirlemektedir:

Bir işlev çağrısıyla bir işleve gönderilen argüman olan ifadelerin değerlendirilmesinden sonra bir yan etki noktası vardır. Programın akışının çağrılan işleve yönlendirilmesi argüman olan ifadelerdeki yan etkilerin tamamlanmasından sonra gerçekleşir.

Yukarıdaki kodda main işlevi içinde f isimli işleve yapılan çağrıya bakalım. Programın çalışma zamanında programın akışı f işlevi içine girmeden x değişkeninin değeri bir azalarak olacaktır.

Bir tam ifadenin (full expression) sonu bir yan etki noktasıdır. Tam ifade bir başka ifadenin alt ifadesi (sub expression) olmayan ifadelerdir. Aşağıdaki ifadeler birer tam ifadedir:
sonlandırıcı atomla biten ifadeler. (ifade deyimlerinin ifadeleri)

ifade deyiminde (expression statement) yer alan x = 5 bir tam ifadedir. Aşağıdaki koda bakalım:

Yukarıdaki main işlevinde y değişkenine 10 değerinin atanması garanti altındadır:

return deyiminin ifadesi bir tam ifadedir:

Yukarıdaki kodda tanımlanan f işlevi çağrıldığında return deyiminin yürütülmesiyle programın akışı f işlevini çağıran koda geri dönmeden global g değişkeninin değeri değişecektir.

if deyiminin parantezi içindeki ifade bir tam ifadedir:

Yukarıdaki main işlevinde if deyiminin doğru kısmında y değikenine 11 değerinin atanması garanti altındadır. if parantezi içindeki ifadenin değerlendirilmesinden sonra bir yan etki noktası vardır.

while parantezinin içindeki ifade bir tam ifadedir.

Yukarıdaki main işlevinde while parantezi içindeki ifadenin değerlendirilmesi sonrasında bir yan etki noktası vardır. Döngü gövdesinde putchar işlevine argüman olarak gönderilen c değişkeninin değeri son çağrılan getchar işlevinin geri dönüş değeridir.
Benzer şekilde do while döngü deyiminin while parantezi içindeki ifade ve switch parantezi içindeki ifade tam ifadelerdir.

for döngü deyiminin iki “;” atomu ile ayrılmış üç ifadesi tam ifadelerdir:

Yukarıdaki for döngü deyiminin çıkışında i değişkeninin değerinin 4 olması garanti altındadır ve kodda bir tanımsız davranış (undefined behavior) söz konusu değildir.

Lojik ve (&&) ile lojik veya (||) işleçlerinin ilk teriminin hesaplanmasından sonra bir yan etki noktası vardır. Çünkü kısa devre davranışı (short circuit behavior) için işlecin sol teriminin önce ele alınması garanti altındadır. Aşağıdaki koda bakalım:

Yukarıdaki kodda lojik ve işlecinin sağ terimi olan ifadede func işlevine x değişkeninin artmış değeri olan 11‘in gönderilmesi garanti altındadır.

Koşul işlecinin (ternary operator) birinci teriminden sonra da bir yan etki noktası var:

Yukarıdaki kodda f1 isimli işlevin x değişkeninin artmış değeri olan 4 değeriyle çağrılması yine garanti altındadır.

Virgül işlecinin sol teriminden sonra bir yan etki noktası söz konusudur:

Yukarıdaki kodda virgül işleciyle kurulan ifade yalnızca konuyu açıklama amaçlı. printf işlevine yapılan çağrıların yanında yazılan, değişkenlerin yeni değerleri yine garanti altındadır.

Bir değişkene ilk değer veren ifadenin (initializer expression) değerlendirilmesinden sonra bir yan etki noktası oluşur:

Yukarıdaki kodda z değişkenine ilk değer veren ifadeden sonra bir yan etki noktası vardır. z değişkeni 31 ilk değerini almadan önce x ve y değişkenleri arttırma ve eksiltme işleminden kaynaklanan yan etkilerle yeni değerlerini alırlar.

Virgüllerle ayrılan listeyle birden fazla değişkenin tanımlanmasında her virgül atomundan sonra bir yan etki noktası oluşur. (Buradaki virgül bir işleç değildir.):

Yukarıdaki kodda x değikenine ilk değer verilmesini izleyen virgül atomundan sonra bir yan etki noktası vardır. değişkenine 11 ilk değerinin verilmesi güvence altındadır.

Yazılan bir ifadede bir yan etkisi noktası söz konusu değil ise işlemlerin yapılma sırası (order of evaluation) ve buna bağlı olarak yan etkilerin gerçekleşme sırası güvence altında değildir. Aşağıdaki koda bakalım:

Yukarıda yer alan ifadede üç ayrı yan etki vardır:

j değişkeninin değerinin 1 artması
i değişkeninin 1 artması
x değişkeninin değerinin değişerek i + (j + 1) olması

Bu yan etkileren hangisinin zamansal olarak daha önce gerçekleşeceği konusunda bir güvence yoktur. Şimdi de aşağıdaki kodu inceleyelim:

Yukarıdaki kodda hem f1 işlevi hem de f2 işlevi global değişken olan g‘nin değerini değiştiriyor. Global değişken g‘nin değerinin değiştirilmesi bir yan etkidir.

ifadesi bir yan etki noktası içermediğinden hangi işlevin daha önce çağrılacağı belirli değildir. Bu durumda main işlevinde x değişkenine hangi değerin atanacağı garanti altında değildir. Şimdi de aşağıdaki koda bakalım:

y değişkenine hangi değer atanacak? y değişkenine hangi değerin atanacağı öngörülemez. Yazılan kod sentaks açısından geçerli olsa da tanımsız davranış (undefined behavior) özelliği gösterir.

İşleç önceliğini bir işlemin zamansal olarak daha önce yapılması ile karıştırmak sık karşılaşılan bir durumdur. Eğer bir yan etki noktası söz konusu değil ise işleç önceliği işlemin daha önce yapılması anlamına gelmez.

Yukarıdaki ifadede f1, f2 ve f3, int türden değer döndüren işlevler, x de int türden bir değişken olsun. Çarpma işleci toplama işlecinden daha yüksek önceliğe sahiptir. Ancak bu f3 ya da f2 işlevinin f1 işlevinden önce çağrılacağı anlamına gelmez. Bir C derleyicisi bu işlevleri herhangi bir sırayla çağıracak kod üretmek konusunda özgürdür (unspecified behavior). İşleç önceliğinin verdiği garanti f2() * f3() ifadesinden hesaplanacak değerin toplama işlecinin sol operandı olacağıdır. Öncelik parantezi de bir işlemin zamansal olarak daha önce gerçekleştirileceği güvencesini sağlamaz. Yukarıdaki kodun bu kez aşağıdaki gibi yazıldığını düşünelim:
f2 ya da f3 işlevinin f1 işlevinden daha önce çağrılması bir garanti altında değildir. Bir C derleyicisi f1 işlevini daha önce çağıracak bir kod üretmek konusunda özgürdür.
işleçleri hariç hiçbir işlecin terimlerinin ele alınması bir yan etki noktası içermez.
Yan etkiye maruz kalmış bir değişkenin bir yan etki noktası geçmeden aynı ifade içinde yeniden kullanılması tanımsız davranış oluşturur.  C dilinde yapılan tipik kodlama hatalarından biri bu tür tanımsız davranışlar oluşturmaktır. Aşağıdaki ifadelerin herbiri tanımsız davranışa sahiptir:

Necati Ergin

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

Bunlar da ilginizi çekebilir

Bir Cevap Yazın

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

Kod Eklemek İçin Okuyun