C'de Sequence Point ve Undefined Behaviour

Herkese merhaba arkadaşlar, dün C Programlama Dersine Giriş dersinin vizesine girdim ve vizede şuna benzer bir soru sordular.

#include <stdio.h>

int main()
{
    int a = 10;
    
    if(a == a++)
        printf("True-1 ");

    a = 10;
    
    if(a == ++a)
        printf ("True-2");

    return 0;
}

Bu kodun çıktısı ne olur?

Ben, output’un

True-1

olduğunu düşündüm ama sınavdan sonra yanlış yaptığımı fark ettim.

Sınavdan sonra kodu şu şekilde derledim.

gcc -std=c99 -Wall -Werror a.c -o a

Ve uyarı verdi, derlemedi. Ama

gcc -std=c99 a.c -o a

Derleyince derledi.

Neyse işin hikayesini belki başkaları buna benzer bir sorun yaşarsa diye anlatmak istedim. Şimdi asıl konuya gelelim.

Yukarıdaki işlemleri yaptıktan sonra bunun Undefined Behaviour olduğunu arkadaşlarla tespit ettik. Bir arkadaşım yukarıda bahsettiğim kodu mac de derleyince farklı, üniversitenin ssh ile bağlandığımız serverinde derleyince farklı bir çıktı verdi.

Bunun üzerine bunun nedenini araştırmaya başladım. Dün saat 21’den bugün saat 7’ye kadar bunun nedenini anlamaya çalıştım ve şöyle bir yazı kaleme aldım.

Ama bir konuyu hala tam anlayamadım. Seqpoint nedir? Biraz tanım biliyorum ama sanırım yeterli değil, yazdığım yazıyı okursanız seqpoint meselesini nasıl anladığımı anlayacaksınız. Ama seqpoint ile ilgili anlayamadığım birkaç şey var.

a[i] = i++
i = ++i
i++ * i++

gibi ifadeler UB(Undefined Behaviour) oluyor. Çünkü

seqpoint a[i] = i++ seqpoint
seqpoint i = ++i seqpoint
seqpoint i++ * i++

C standardını yanlış anlamadıysam şöyle diyor: iki seqpoint arasında bir değişkenin değeri en fazla bir kez değiştirilebilir.

Ama şu UB değil

int i = 0;
++i && i--

çünkü

seqpoint int i = 0; seqpoint
seqpoint ++i seqpoint i--

Yani && operatörünün olduğu yer seqpoint oluyor. Bu yüzden de her iki seqpoint arasında i değişkeni sadece bir kez modifiye ediliyor.

Şimdi asıl soruya gelmek istiyorum.

Eğer

i++ && i++

UB değil ama neden aşağıdaki ifade UB’dir?

#include <stdio.h>

void f(int x, int y)
{
    printf ("%d %d\n", x, y);
}
int main()
{
    int i = 0;
    f(++i, ++i);
    return;
}

Yani C standartlarında || && , ?: operatörlerinin olduğu yer seqpoint ise neden

f(++i seqpoint ++i)

Şeklinde olmuyor veya oluyorsa neden UB veriyor.

Sorularımı toparlıyorum:

  • Seqpoint tam olarak nedir?
  • C standardındaki şu yazılar bize tam olarak ne demek istiyor, ne anlamamız gerekir? link

The Standard states that
Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.

  • Standartta geçen tanımları kullanarak bir ifadenin UB olduğunu nasıl anlarız?

Yardımlarınız için şimdiden teşekkür ederim.

1 Beğeni

Merhaba.

Yazdığınız yazıda gözüme çarpan birkaç hatayı düzeltmek istiyorum.

ìnt a, b; ifadesindeki virgül bir operatör görevi görmüyor, bu yüzden bir seqpoint değil. Operatör olan virgüle şunu örnek verebiliriz:

#include <stdio.h>

int main()
{
    int a = (1, 2);
    printf("%i\n", a); // 2
}

Daha sonra yazınızda şöyle bir yer geçiyor:

Bu kod Undefined Behaviour’dur. Çünkü derleyici GeeksforGeeks de bastırabilir, forGeeksGeeks de bastırabilir. Bunun nedeni şu, hatırlıyor musunuz hangi operatörler sequence pointti?

Bu undefined değil, unspecified behaviour. İlk önce f1’in mi yoksa f2’nin mi çalışacağı belli değil. Ama ikisinin de çalışacağını biliyoruz, sadece çalışma sıraları standart tarafından belirtilmemiş. Oysa undefined behaviour bundan daha tehlikeli, eğer burada undefined behaviour olsaydı derleyici hiçbir şey yazdırmamaya veya dosyalarınızı silmeye de karar verebilirdi.

Undefined behaviour’dur. Çünkü == , C standartlarında tanımlı bir sequence point değil. Bu yüzden önce ++a expressionu nun mu çalışacağını, yoksa `a expressionunun mu çalışacağı derleyiciden derleyiciye göre değişir.

Burada da undefined behaviour olduğu için herhangi bir şey olabilir. Olasılıklar a’nın mı yoksa ++a’nın mı önce çalışacağından ibaret değil.


En başta bahsettiğim gibi buradaki virgül bir operatör değil, fonksiyon çağırma söz diziminin bir parçası. Bu yüzden bu kod undefined behaviour içeriyor.

2 Beğeni

Sağ olasın hocam, aslına bakarsanız unspecified behaviour u daha önce duymamıştım. Undefined behaviour ile de ilk defa uğraşıyorum. Bilgilendirmelerin için gerçekten çok teşekkür ederim. O zaman şimdi bana undefined behaviour ve unspecified behaviour ifadeleri tam olarak ne anlama geliyor detaylı bir araştırma yapmak düşer.

Şu seqpoint’e de sizin cevabınızdan sonra tekrar göz atacağım, muhtemelen bu sefer işim daha da kolay olacak sayenizde.

Değerli vaktinizi ayırdığınız için çok teşekkür ederim.

Hayırlı akşamlar.

1 Beğeni

Her sey olabilir. Standart her sey olmasina izin veriyor.

Muhtemelen a 1 artar ve if’e girer veya girmez.

Ayni makinada birkac kez calistirinca da farkli sonuclar verebilir.

Sequence point uzayda degil zamanda tanimli bir nokta. Expression’in sagina-soluna koyarak anlayamaz, anlatamazsin. Kodun neden oldugu dogru ve yan etkileri siraya dizmen lazim. Sequence point’in oncesindeki etkiler, sonrasindaki etkilerden once oluyor.

Cunku yukarida iki i++ arasinda sequence point var. Asagida yok.

Standardi okuyarak. Dogrudan “undefined” [behavior] ifadesi geciyor. Cokca kez.

Kod akisini degistiren operatorlerde, karar vermeden hemen once var.

2 Beğeni