Kodumun Sadece Belirli Bir Kısmı Çalışıyor

Merhaba,
c dilinde bir sayı tahmin oyunu yazmaya çalıştım. Ve bitirdim, bir çok hatayla karşılaşmama rağmen. Ama şimdi de bu hata ile karşılaştım hata maalesef görünmüyor. Sadece kodu ve çıktıyı verebileceğim.

kod :

#include <stdio.h>
#include <windows.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {
    char *metin1 = "merhaba,";
    char *metin2 = "tarafindan yapilan sayi tahmin oyununa hosgeldiniz.";
    char *metin3 = "uyari : bu oyun egitim amaci ile yapilmistir ve tamamen acik kaynak kodludur.";
    // evet yazıları değişken olarak kaydettik çünkü onları havalkı bir biçimde ekrana getirmek için onlara değişken olarak ihtiyacımız var
    int i;
    for(i = 0; i < strlen(metin1); i++) {
        printf("%c", metin1[i]);
        
    }
    printf("\n");
    int a;
    for(a = 0; a < strlen(metin2); a++) {
        printf("%c", metin2[a]);
        
    }
    printf("\n");
    int p;
    for(p = 0; p < strlen(metin3); p++) {
        printf("%c", metin3[p]);
        
    }
    printf("\n");
    /*
    evet burada yazılar havalı bir şekilde geliyor. Bunun nasıl olduğunu sitemde detaylı bir şekilde anlatacağım.
    */
    printf("evet sayi tutuldu lutfen tahmin ediniz.(sayi 0 ile 300 arasinda)\n");
    int sayi = rand()%300;
    int bulundumu;
    while (bulundumu == 1) {
        int tahmin;
        printf("bir sayi tahmin ediniz : ");
        scanf("%d", &tahmin);
        printf("\n");

        if(tahmin == sayi) {
            printf("tebrikler, sayi %d idi", tahmin);
            bulundumu == 1;
        }
        if(tahmin > sayi) {
            printf("tahmininiz sayidan buyuk");
        }
        if(tahmin < sayi) {
            printf("tahmininiz sayidan kucuk");
        }
    }
    
    return 0;
}

çıktı :

C:\Users\TERM>a.exe
merhaba,
tarafindan yapilan sayi tahmin oyununa hosgeldiniz.
uyari : bu oyun egitim amaci ile yapilmistir ve tamamen acik kaynak kodludur.
evet sayi tutuldu lutfen tahmin ediniz.(sayi 0 ile 300 arasinda)

C:\Users\TERM>
 int bulundumu;

İlk değer verilmemiş bu birinci sorun…

bulundumu=1;

Yapın.

while koşuluna göre bulununca değeri sıfırlayın:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {
    char *metin1 = "merhaba,";
    char *metin2 = "tarafindan yapilan sayi tahmin oyununa hosgeldiniz.";
    char *metin3 = "uyari : bu oyun egitim amaci ile yapilmistir ve tamamen acik kaynak kodludur.";
    // evet yazıları değişken olarak kaydettik çünkü onları havalkı bir biçimde ekrana getirmek için onlara değişken olarak ihtiyacımız var
    int i;
    for(i = 0; i < strlen(metin1); i++) {
        printf("%c", metin1[i]);
        
    }
    printf("\n");
    int a;
    for(a = 0; a < strlen(metin2); a++) {
        printf("%c", metin2[a]);
        
    }
    printf("\n");
    int p;
    for(p = 0; p < strlen(metin3); p++) {
        printf("%c", metin3[p]);
        
    }
    printf("\n");
    /*
    evet burada yazılar havalı bir şekilde geliyor. Bunun nasıl olduğunu sitemde detaylı bir şekilde anlatacağım.
    */
    printf("evet sayi tutuldu lutfen tahmin ediniz.(sayi 0 ile 300 arasinda)\n");
    int sayi = rand()%300;
    int bulundumu=1;
    while (bulundumu == 1) {
        int tahmin;
        printf("bir sayi tahmin ediniz : ");
        scanf("%d", &tahmin);
        printf("\n");

        if(tahmin == sayi) {
            printf("tebrikler, sayi %d idi", tahmin);
            bulundumu = 0;
        }
        if(tahmin > sayi) {
            printf("tahmininiz sayidan buyuk");
        }
        if(tahmin < sayi) {
            printf("tahmininiz sayidan kucuk");
        }
    }
    
    return 0;
}

Sağolun, evet kod çalışıyor. Sanırım basit bir hata yüzünden, ama neden bir hata vermek yerine oradan sonraki kodlar çalışmıyor ?

Buradaki hata bana modern bir derleyicinin fark edebileceği kadar basit gözüktü, kodunuzu hangi komut ile derliyorsunuz? Derleyicinizin bütün uyarılarını etkinleştirmeyi deneyebilirsiniz.

Bu tür hatalara kolayca düşebilmeniz dilin yapısından, bu hataların önemli bir kısmının ancak uygulama derlendikten sonra kendini göstermesi ise bu tür hataların tamamının derleme esnasında bulunmasının imkansız olmasından kaynaklanıyor.

1 Beğeni

Hata yüzünden demeyelim, detaylıca açıklamaya çalışayım.

Çünkü ortada bir hata yok derleyiciye göre.

Şöyle anlatayım, hiç bir değişkene ilk değer vermek zorunda değilsiniz. Bu bir hata değildir. Ama iyi bir alışkanlık olarak ilk değer vermek iyidir.

Ben neden ilk değer verdim oradan başlayım.

Çünkü kodunuzun kalan kısmını while döngüsü üzerine kurmuşsunuz.

Yani,

while (bulundumu == 1) {

Kod satırınız şunu söyler, bulundu mu 1 olduğu sürece bu kısmı tekrar tekrar çalıştır.

Ama sizin kodunuz bunun öncesinde bulundumu kısmına hiç 1 atamamış bu nedenle asla while bloğu içindeki kod çalıştırılmaz.

Yani burada derleyici açısından bir hata yok. Hata ve uyarılar çeşit çeşittir.

Bu tür sizin son kodunuzdaki gibi olanlara mantık hatası denir, genelde pek mantık hatları ile ilgillenmezler ne yapmak istiyorsanız onu yaparlar.

Çünkü yazdığınız kodun c dili açısından bir hatası yok

Siz kontrol koşuluna değer vermeden koşulu konrol ederseniz koşulu sağlamaz ve pas geçer.

Bu c nin tabiatında vardır. Öyle kolay değildir ilk değer atanmamış bir koda hatalı demek, çünkü c bellekte adresler kullanarak pointerlar la da değer atayabilir ve ancak çalışma zamanında bunlar atanmış olabilir. Bu tür bir duruma izin verebilmesi için kodun ilk değer atamalarını görmezden gelebilir.

Yani C silah gibi bir dildir, düzgün kullanamazsanız elinizde patlayabilir.

Ama asıl sorun dediğim gibi ilk değer atanmaması değil aslında sizin while ile yanlış şekilde kontrol ediyor olmanız.

İlk değer atanmamış bir kodu bırakıyorum alta bu şekilde de çalışır…

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {
    char *metin1 = "merhaba,";
    char *metin2 = "tarafindan yapilan sayi tahmin oyununa hosgeldiniz.";
    char *metin3 = "uyari : bu oyun egitim amaci ile yapilmistir ve tamamen acik kaynak kodludur.";
    // evet yazıları değişken olarak kaydettik çünkü onları havalkı bir biçimde ekrana getirmek için onlara değişken olarak ihtiyacımız var
    int i;
    for(i = 0; i < strlen(metin1); i++) {
        printf("%c", metin1[i]);
        
    }
    printf("\n");
    int a;
    for(a = 0; a < strlen(metin2); a++) {
        printf("%c", metin2[a]);
        
    }
    printf("\n");
    int p;
    for(p = 0; p < strlen(metin3); p++) {
        printf("%c", metin3[p]);
        
    }
    printf("\n");
    /*
    evet burada yazılar havalı bir şekilde geliyor. Bunun nasıl olduğunu sitemde detaylı bir şekilde anlatacağım.
    */
    printf("evet sayi tutuldu lutfen tahmin ediniz.(sayi 0 ile 300 arasinda)\n");
    int sayi = rand()%300;
    
    printf("%d\n",sayi);
    int bulundumu;
    while (bulundumu != 1) {
        int tahmin;
        printf("bir sayi tahmin ediniz : ");
        scanf("%d", &tahmin);
        printf("\n");

        if(tahmin == sayi) {
            printf("tebrikler, sayi %d idi", tahmin);
            bulundumu = 1;
        }
        if(tahmin > sayi) {
            printf("tahmininiz sayidan buyuk");
        }
        if(tahmin < sayi) {
            printf("tahmininiz sayidan kucuk");
        }
    }
    
    return 0;
}

Sadece koda bir printf ile tahmin etmem gereken sayıyı da baştan yazdırdım, kontrol ederken sayıyı tahmin etmekle uğraşmamak için.

Bu halinde bulundumu 1 olmadığı sürece while bloğundaki kod işletilir. Sayı bulunursa bulundumu değeri 1 olur ve koşul bozulduğu için döngüden çıkılır.

Hatalar syntax hatası olursa yakalamak kolaydır, ama programcının hatalı düşüncesini kolay yakalayamaz c.

Bu doğru değil, while bloğunun çalışması mümkün. Açıklaması verdiğim linkte var.

Buradaki problem bir logic error’dan kaynaklı değil, dilin kuralları ihlal ediliyor.

Var. Undefined behaviour’a sebep oluyor, syntax hatasından aşağı kalır bir yanı yok.

Bu söylem aşağıda verdiğiniz örneğin doğru çalıştığı iddası ile çelişiyor.

Sorun değer atanmamış bir değişkenin değerine erişilmeye çalışılması.

Belki istenilen sonucu verir. Vermek zorunda değil çünkü bu kod da yanlış. Dediğim gibi linkte açıklaması var.

Okudum sn EkremDincel.

Size katılmıyorum.

while bloğununun çalışması konusunda hele hiç katılamıyorum. Borland C derleycilerinden, microsoft c derleyicilerine kadar bir çok derleyici ile denediğim kodlar oldu.

Çoğu siz tanımlamazsanız dahi en azından null a atayarak derliyor yani random bir ilk değer uzun yıllardır almıyor.

Burada da hiç katılmıyorum. Ansi C standartları ki çoğunda değişiklik yok, İlk değer atama zorunluluğu yok. Yani dilin kuralı ilk değer atamayı zorunlu tutmuyor.

Karşılaştırma koşuluna gelince 1 olduğu sürece çalışmasını istediğiniz bir kod bloğuna 1 göndermediğiniz sürece neden çalışsın? Yada bunda neden bir hata olsun? Tanımlamayı yaptınız değişkeni ve oda tanımladı ve null olarak atadı. null bir değişken i siz tekrar yüklemeden koşulda karşılaştırmaya sokarsanız bu sizin sorumluluğunuzdadır.

Buradaki durum C de izin verilen bir durum, gayet açık anlattığımı düşünüyorum. Sizin kodunuzda ilk değer vermemiş olmanız, birden çok C dosyasından oluşan veya başka bellek bölgelerinden çağırılan kodların bu değişkene sonrasında değer atamadığı anlamına gelmez.

Fonksiyon ve fonksiyon prototipleri gibi düşünün. prototipi olan bir fonksiyon başka bir dosyada olabilir. Bu o fonksiyonun olup olmamasını kontrol etmeyeceği anlamına gelir.

Hayır çelişmiyor.

Şöyle ki ilk kodda while 1 durumda çalışıyor ve o ana kadar hiç 1 olmamış olduğundan while döngüsünde işlem görmüyor.

İkinci kodda ise yukarıdan 1 değeri atanmadığını biliyoruz, çünkü ilk değer atamadık. Bu nedenle ne geldiği değil ne gelmediğini kontrol ediyoruz:

while (bulundumu != 1) {

1 olmadığı sürece çalış, neden? Çünkü yukarıda 1 atamadık.

İlk kodda durum bu değildi, 1 olduğu sürece çalış şeklinde idi.

İki kodun değiştirdiğim kısımlarına dikkatle tekrar bakmak isteyebilirsiniz.

Bu konuda C her zaman serbest bırakır, belleğin hangi bölgesine erişmek isterseniz ne yaptığınızı biliyorsanız yapın der.

Belki demeyelim, kodları derlemeden denemeden paylaşmamaya gayret gösteriyorum. C bellek ayırdıktan sonra ilk değer içerip içermediğiyle ilgilenmez. Ayırdığı belleği derleyici ayarları açıksa null a eşitler, kapalıysa o bellek bölgesinde hangi değer varsa ona bırakır.

Bu nedenle ben yanlış diyemiyorum.

Okudum linkteki durumu. Şu an onunla tam benzeştiremedim.

Nedenini özetlemem gerekirsek, değişken ve değişken adresleriyle çalışabilen bir dilde, değişkene atama yapmamanız, değişken adresi üzerinde atanmadığı anlamına gelmeyeceğinden böyle durumla C izin verir.

Yani işaretçiler bellek bölgesine ulaşıp değer vermiş olabilir varsayımıyla ilk değer atamamasını en fazla warning olarak değerlendirebilir.

İsterseniz sondan başa doğru derli toplu bir halde bir kez daha açıklayayım.

İlk değer atamadım ve ilk değer atamadığım bir değişkeni yazdırdım. C buna itiraz etmez.

Bu bir online derleyici, bellek bölgesi boş ise 0 yazmış olabilir. Yada kendi null atamış olabilir. Sonuç bu.

Mantık hatasını açıklayayım.

İlk koduna bakarsanız;

Bu kodun fazlalıklarını kırptım.

Sadece isteğiniz üzerine ilk değer atadım: İlk değer = 0,

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main() {

    printf("evet sayi tutuldu lutfen tahmin ediniz.(sayi 0 ile 300 arasinda)\n");
    int sayi = rand()%300;
    
    printf("%d\n",sayi);
    int bulundumu=0;
    
    while (bulundumu == 1) {
        int tahmin;
        printf("bir sayi tahmin ediniz : ");
        scanf("%d", &tahmin);
        printf("\n");

        if(tahmin == sayi) {
            printf("tebrikler, sayi %d idi", tahmin);
            bulundumu = 1;
        }
        if(tahmin > sayi) {
            printf("tahmininiz sayidan buyuk");
        }
        if(tahmin < sayi) {
            printf("tahmininiz sayidan kucuk");
        }
    }
    
    return 0;
}

While bloğu çalışmayacaktır. çünkü ilk değer 0 ama while sadece 1 olduğu sürece çalışacaktır.

Yani asla. Bu nedenle mantık hatası var.

Diğer taraftan while koşulunun çalışması için değiştirdiğim kodu verdim burada, gelen değerin ne olduğu önemli değil 1 olmadığı sürece çalışır. Taki while bloğu içinde değeri 1 olduğunda bloktan çıkar.

Bu tür hata bildiğimiz mantık hatası.

İsterseniz elinizdeki farklı editörlerde bu mesajda verdiğim kodu deneyerek farklı durumları değerlendirelim.

Yani soru sahibinin asıl sorunu ilk değer vermemesi değil, while bloğunun mantık hatası yüzünden asla çalışmayacak olması.

Bu söyleminizden link verdiğim yazımı ve yazı içerisindeki referansları okumadığınız sonucuna varıyorum.

Ben böyle bir zorunluluk olduğunu söylemedim zaten. Sorunun sebebini de yazdım:


Cevabı linkte var.

Null ile sıfırı kastettiğinizi varsayıyorum. C dilinin tanımlanma anında değer atanmayan değişkenlere null değeri atama gibi bir kuralı, özelliği yok. Derleyiciler canı isterse yapabilir. Canı isterse yapmaz.

Linkte de örneği var, derleyiciler bu dediğinize katılmıyor.

Bu doğru değil ve müsait olduğumda gcc ve clang ile bu söyleminizi yanlışlayan örnekler verebilirim. Gerçi verdiğim linkteki referanslara baksaydınız zaten birkaçını görürdünüz.

Hangi ayarlardan bahsediyorsunuz? Bu olsa olsa standart olmayan bir derleyici eklentisidir. Biz şu anda C hakkında konuşuyoruz, bir derleyicinin modifiye ettiği C dili ile alakalı veya sadece bir derleyici ile birlikte kullanıcak bir C kodu ile alakalı değil.


Birkaç defa derleyicilere gönderme yaptım. Aslında C standartına gönderme yapmam teknik olarak daha doğru olur ama zaten gcc, clang gibi derleyiciler standartı oldukça iyi takip ediyor. Hem dilin spefikasyonuna baksaydınız ortada bir tartışma olmazdı ama siz “kodu derledim, denedim, çalışıyor” gibi bir yöntem izlediğiniz için ben de onu izleyeceğim.

Okudum ama özellikle tekrar bakmamı istediğiniz bir yer varsa link verirseniz onu da tekrar incelerim.

Hayır while döngüsüne girmeme sorunun sebebi ilk değer olmaması değil, yanlış koşul ile while döngüsü açılması. Örneği yukarıda, sonsuz bir while döngüsü açarak, break komutu ile de yapsa olurdu, bu tamamen programcı yaklaşımından kaynaklı bir mantık hatası.

Buyrun size bir önceki mesajımda ilk değer 0 verdim yine while bloku çalışmaz. Yani ilk değer verip vermemekten ziyade kurgu ile alakalı.

Gerçekten nereyi özellikle tekrar okumam gerekiyor merak ettim verirseniz sevinirim.

Null ile sıfırı kastetmiyorum, işaretçi olan sıfır ile sıfır aynı şey değildir. Canı isteme durumu derleyicilerde optimizasyon seçenekleri, debug level ayarları ile compiler ın diğer ayarları ile alakalı iddialı olmamak lazım borland derleyicisi kullandınız mı hiç? Bir bakın isterseniz.

Linkte uzun bir çalışma yapayım size sonuçları paylaşayım.

Açıkçası gcc ile de dener sonuçları paylaşırım. Ama windows sevdiğim için, cygwin falan üzerinde bakabiliriz.

Standart C Ansi 99 dan itibaren hepsi…

C standartlarını okumamış ve takip etmemiş olduğumu düşünmeniz üzücü. Ama size felsefeyi anlatamadım sanırım.

Bellek erişimi yüksek bir dil neden ilk değer atamasını dert etsin? O bellek adresine farklı şekillerde erişilip değiştirilmiş olabilir bu benim sorunum değil şeklinde bakar.

Undefined behaviour konusunda zaten olası ve yadırganmayan bir durum olduğundan ilk değer atamayı tavsiye ettim. Fakat bu beklenen bir durum olduğundan hata buna bağlanmaz.

Daha nasıl anlatırım bilemedim.

Bir kaç farklı derleyici üzerinde inceleyim, sizin verdiğiniz linklere de bakayım neyi gözden kaçırdığımı düşündünüz üzerinde duralım.

Belleğin hangi bölgesine işaret ettiği belli olmayan içeriği ne olduğu belli olmayan bir değişkenin davranışının tahmin edilemez olması durumu çok yadırganacak bir durum değil.

Bunu size nasıl örneklerim ona da ayrıca bakalım.

EDIT:

Sizden özelinde bakmamı istediğiniz linki bekliyorum fırsatınız olursa vurguladığınız kısmı paylaşın çünkü nereden bahsettiğinizi hala anlayamadım.

Aslında ikimiz de aynı şeyden bahsediyoruz, ama ayrıştığımız yer konunun mantık hatası olma kısmında.

Diğer taraftan,

Boş durmayım iki derleyici deneyim dedim…

Elimde eski bir gcc derleyicis var, pek sevmem ama DevC++ içindeki gcc.

Onda da bu hatayı almadım mesela.

Yine bir de visual studio da baktım.

En azından hata olarak yakalaybildi.

Dahası ;

c++ - How to ignore uninitialized variable error in MSVC - Stack Overflow

Böyle yapınca arkadaş benim bahsettiğim gibi sorun yaşamış, bereket /sdl ile kapatılabilen bir ayar.

Her kontrol bir kısıtlamadır.

Varsa varklı derleyicleri de denemeye devam edeceğim.

Özellikle eski borlandları bulabilirsem onlarda bakmak,
Sonrasında Codegear in C Builder ı yaşıyorsa onda da denemek istiyorum.

Başa döneyim. İlk baktığımda soruyu soran bir online derleyici kulllamış, ben de benzerind denedim sorun yok,

Eski derleyiciler de sorun çıkacağını düşünmüyorum.

Muhtemel yeni gcc lerde uyarı ve hata verebilir ama bu da kaldırılabilir bir hata uyarısı.

Hala sizinle hem fikir olamadım. Koddaki while a girmeme sorunu mantık hatası. Sizin takıldığınız kısım detay ve farklı bir konu.

EDIT2:
Undefined behavior can literally erase your hard disk - Mature Pessimizations - A programming blog by Theodoros Chatzigiannakis (tchatzigiannakis.com)

Ekrem Hocam bundan bahsediyor olamazsın değil mi?

“It is no secret that C and C++ are chock-full of pitfalls of undefined behavior. The C++ standard describes it as:”

^Evet cümlenin yarısına kadar katılıyorum c ve c++ da undefined behavior olabilir. Ama ikinci cümle?
C++ standardı bunu şöyle tanımlar dediği an okumayı bıraktım…

Biz C++ konuşmuyoruz ki.

C dosyaları ile C++ dosyalarının uzantıları dahi farklıdır ki derlerken sorun çıkarabilir standart uyumsuzlukları.

C bahsettiğim şekilde izin veriyor. Siz bence C++ ile C derleyicilerinin bir arada olmasından biraz karıştırıyorsunuz.

Tekrarlıyorum, hataya itirazım yok, ama koddaki sorun bu değil. Ki zaten bu tip hataların önüne geçmek için zaten ilk değer atamayı tavsiye ettim ve tekrarlıyorum zorunluluk yok.

Örneğide stackoverflowda var, bazen dinamik olarak gerekiyor ve bu nedenle bu bir hata değil.

Ama siz işi C++ a getirirseniz,

İş değişir. C ve C++ on dosya yapılarından açıklama bloklarına, kullandığı kütüphanelere kadar her şey farklı ikisini aynı potada değerlendiremem.

EDIT 3:

Ben yine C üzerinden incelemeye devam edeyim.

C Language Tutorial => Undefined behavior (riptutorial.com)

C11 de bu durum ifade edilmişti ( C11 standard (ISO/IEC 9899:2011)) diye hatırlıyorum. Evet ifade edilmiş.

Ve fakat, editöre bırakılmış.

When the compiler encounters [a given undefined construct] it is legal for it to make demons fly out of your nose” (the implication is that the compiler may choose any arbitrarily bizarre way to interpret the code without violating the ANSI C standard)

Yani C buna izin veriyor, ama derleyicilere bu konuda uyarı ve hata oluşturmalarına karışmamış.

Yine benim bahsettiğim gibi,

In the case of null-pointer dereference, C language differs from managed languages such as Java or C#, where the behavior of null-pointer dereference is defined: an exception is thrown, at the exact time (NullPointerException in Java, NullReferenceException in C#), thus those coming from Java or C# might incorrectly believe that in such a case, a C program must crash, with or without the issuance of a diagnostic message.

Diğer dillerden farklı olarak mesaj vererek veya vermeden çökebileceğini belirtmiş.

Sonraki standartlara da bakalım. C için zorlayıcı hüküm henüz görmedim.

EDIT 4:

Ekrem hocam sağ olsun nezaket gösterip, linki özelden atmış, müsait olduğunda da cevaplayacakmış.

Bu arada linki burada vereyim:

“What The Hardware Does” is not What Your Program Does: Uninitialized Memory (ralfj.de)

Dilim döndüğünce google translate’in de çevirdiğince buraya çevirsinden parçalar bırakayım.

The pitfalls of uninitialized memory
When you allocate some memory (whether it be on the stack or heap), and you do not initialize it, what are its contents? We call this “uninitialized memory”, but what exactly does this mean and what happens when it gets read? For many languages, this question is inconsequential: in Java, Haskell, OCaml and generally in all safe languages, uninitialized memory cannot be read, this is prevented by the type system. The same is true in safe Rust, actually. However, in unsafe Rust as well as in inherently unsafe languages such as C and C++, not having to initialize memory can be an important optimization, so this is a very relevant question.

The C and C++ specifications (without going into all the detail here) say that uninitialized memory is “indeterminate”, but the details of what exactly that means are unclear. Many people will tell you that “uninitialized memory contains a random bit pattern”. This is wrong. They might also talk about the system allocator or how the OS kernel allocates pages for the program to use. That is just irrelevant information.2
Başlatılmamış belleğin tuzakları
Bir miktar bellek ayırdığınızda (ister yığında ister yığında olsun) ve onu başlatmadığınızda, içeriği nedir? Buna “başlatılmamış bellek” diyoruz, ancak bu tam olarak ne anlama geliyor ve okunduğunda ne oluyor? Birçok dil için bu soru önemsizdir: Java, Haskell, OCaml ve genel olarak tüm güvenli dillerde başlatılmamış bellek okunamaz, bu tür sistemi tarafından engellenir. Aynısı aslında güvenli Rust için de geçerlidir. Ancak, güvenli olmayan Rust'ta ve C ve C++ gibi doğası gereği güvenli olmayan dillerde, belleği başlatmak zorunda olmamak önemli bir optimizasyon olabilir, bu nedenle bu çok alakalı bir sorudur.

C ve C++ özellikleri (burada tüm ayrıntılara girmeden), başlatılmamış belleğin "belirsiz" olduğunu söylüyor, ancak bunun tam olarak ne anlama geldiğine dair ayrıntılar belirsiz. Birçok kişi size “başlatılmamış hafızanın rastgele bir bit deseni içerdiğini” söyleyecektir. Bu yanlış. Ayrıca sistem ayırıcısı veya işletim sistemi çekirdeğinin programın kullanması için sayfaları nasıl ayırdığı hakkında da konuşabilirler. Bu sadece alakasız bilgi.2

Buradan anladığım, C’nin daha önce belirttiğim gibi başlatılmamış belleğin bir sorun/hata olarak kabul edilmediği buna dilin izin verdiği ki ben de böyle düşünüyorum. Kimileri bunu okuma hatası olarak kabul ediyor.

Now that we have a concrete way to talk about uninitialized memory, we can talk about which operations are allowed on values involving uninitialized bytes. My interpretation of the rules in C/C++, and my proposal for the rules in Rust, is that any operation working on the “value” of an integer (arithmetic and logical operations, comparisons, conditional jumps) is undefined behavior if any input is uninitialized. In particular, x + 0 is UB if x is not initialized. However, this still leaves many questions open, such as whether even just creating an uninitialized u8 is undefined behavior in Rust (which is the subject of active discussion), or what happens when some but not all bytes of the input are uninitialized. Over time, we will come to some kind of compromise here. The important part (for both Rust and C/C++) however is that we have this discussion with a clear mental model in our minds for what uninitialized memory is. I see Rust on a good path here; I hope the C/C++ committees will eventually follow suit.
Artık başlatılmamış bellek hakkında konuşmanın somut bir yolu olduğuna göre, başlatılmamış baytları içeren değerler üzerinde hangi işlemlere izin verildiğinden bahsedebiliriz. C/C++'daki kuralları yorumlamam ve Rust'taki kurallar için önerim, bir tamsayının "değeri" üzerinde çalışan herhangi bir işlemin (aritmetik ve mantıksal işlemler, karşılaştırmalar, koşullu atlamalar) herhangi bir girdi olması durumunda tanımsız davranış olduğudur. başlatılmamış. Özellikle, x başlatılmazsa x + 0 UB'dir. Bununla birlikte, bu hala, başlatılmamış bir u8 oluşturmanın bile Rust'ta tanımsız bir davranış olup olmadığı (etkin tartışmanın konusudur) veya girdinin tüm baytlarının değil bazılarının başlatılmadığında ne olduğu gibi birçok soruyu açık bırakmaktadır. Zamanla, burada bir çeşit uzlaşmaya varacağız. Ancak önemli olan kısım (hem Rust hem de C/C++ için), başlatılmamış hafızanın ne olduğuna dair zihnimizde net bir zihinsel modelle bu tartışmayı yapmamızdır. Rust'ı burada iyi bir yolda görüyorum; Umarım C/C++ komiteleri sonunda davayı takip eder.

Burada google translate’ten aynen çevirdim çok gözüme batan kelime olmadı. Ve zaten C/C++ komitesi de henüz bu konuda karara varmamış. En azından yazının yazıldığı 2019 tarihine kadar.

Tamamına detayla bakabiliriz benim de vaktim olursa.

Bazı kısımlarını alıntıladığım yazıyı detayları ile okuyorum.

Katılmadığım çok kısım var. Tabi Ekrem hocamın söyleyeceklerine de bakarız. Ama C komitesinin hem fikir olup karara bağlamadığı bir konuyu biz burada çözebilir miyiz bilemem.

Ben şimdilik yazarın söyledikleri üzerinden devam etmek istiyorum.

C varolduğundan beri, tip korumalı olmayan, aynı zamanda belek ayırma kontrolü yapmayan, dizi ve işaretçilerin taşmalarını denetlemeyen bir dil. Bu şekilde tasarlanmasının mantığını da anlayabiliyorum. Sonuçta bir işletim sistemi yazmak için tasarlanmış. Bir den çok kod, farklı farklı çalışırken belleğin farklı bölgelerine ortak erişmek ortak değişken ve datalarla çalıştırılmak istenmiş olabilir.

Yani flag ler ile yönetilen bir kesmeye birden çok c kodu erişmek ve değiştrimek isteyebilir. Bu değişken farklı bir yerde declare edilmiş ama bir başka program tarafından da erişilebilir olması gereken koşullar oluşuğunda bir bellek ayırmak yerine mevcut daha önce ayrılmış bellek adresine eriştiğini düşünmüş olabilir.

Olasılıklar sınırsız.

Tabi ki bu durumda hata riski her zaman vardır.

Ekrem hocamın bahsettiği Undefined behavior, çevirirsek tanımsız davranış bana kalsa “Öngörülemeyen Durum” (unpredictable situation ) demek tercihim olurdu ki çeşitleri standartta listelenmiş.

İlgilenenler için:

WG14/N1256 Septermber 7, 2007 ISO/IEC 9899:TC3 (port70.net)

Farklı kaynaklardan da ulaşılabilir.

Baştan beri söylediğim gibi, C bu tür durumlara dil olak doğası gereği, ihtiyaç da olduğundan izin veren bir dildir. Çünkü ne yaptığını bilen biri istisna da olsa bu tür ihtyaçlar duyabilmektedir.

Peki yazarın hatası nerede?

Basit, C’nin sadece bir PC üzerinde çalıştırıldığını düşünüyor olması. (Bu benim düşüncem) İşletim sistemlerinin de bellek korumalı olduğu düşüncesi.

Oysaki işlemciler protected mode dışında extended modda da çalışıtırılır ve her çalışma moduna göre bellek yönetimi farklıdır.

What is protected mode? - Definition from WhatIs.com (techtarget.com)

Bu da yetmezmiş gibi işletim sistemleri de kendi bellek yönetim yordamlarını kullandıklarından, bir işletim sistemi üzerinde çalışan programlar( bizim zamanımızda programı koşturmak deniyordu, run’dan geliyor sanırım.) zaden doğrudan donanımsal belleğe kontrolsüz erişemez.

Yani?

Yanisi şu. Aslında doğrudan işlemci tarafından çalıştırılan bir c kodu işle bir işletim sistemi üzerinde çalıştırılan C kodu aynı adres problemlerini yaşamaz. Çünkü aslında adresleri işletim sistemleri yönetir.

C de bahsedilen tanımsız davranış kavramı bu nedenle işletim sistemleri üzerindeki bir c kodunda null veya 0 döndürebilir. Çünkü işletim sistemi gerçekte ram adresine asla erişim izni sağlamaz.

Sanal hafıza teknikleri sıkça kullanılır:
Virtual memory - Wikipedia

Bu nedenle işletim sistemi, eğer bir geçersiz adres isteği alırsa vereceği değer C ye değil işletim sistemine bağldır.

Ama saf c kodu ile korumalı modda c işlemciye tam yetki ile erişiyorsa, yaptığı çağrılar mutlaka ram in rastgele bölgesindeki değerlere gidecektir.

Gitmiştir tecrübeyle sabittir. Gerçek eski DOS işletim sistemini kullananlar bunun farkını anlayacaklardır.

Ama şimdiki işletim sistemleri doğrudan ram e erişmeye izin vermediklerinden ve bellek yönetimini kendileri yaptığında, geçersiz çağrılara kendilerine göre cevap verecektir.

Şimdilik bu kısmı uzatmayım.

C’nin gömülü sistemlerden, işletim sistemlerine, masa üstü uygulalamarına, oyun programlarına göre geniş bir alana hitap ettiğini düşünürsek, daraltılmış kuralların her zaman doğru yaklaşım olmayacağı anlaşılacaktır.

Ekrem hocamın, başta belirttiği, gelişmiş/güncel bir c derleyicisi kullanmak ve uyarı ve hata seçeneklerinin tamamını açmak tavsiyesine katılıyorum. Bu durumda bu tür hatalar yakalanabilir.

Ama bu C nin doğasına aykırıdır. Sadece masaüstü uygulamalarda belki yeri vardır. Ve henüz bu konuda editörler ve derleyicileri zorlayan bir standart görmedim. Yeni standardı da para ile indirmek ve okumak istemiyorum.

Microsoft Visual Stduio 2022 indirdim, sdl seçenekleri açık ve kapalı olarak kodu çalıştırdım.

/sdl (Ek Güvenlik Denetimlerini Etkinleştir) | Microsoft Docs

Yani bu denetimler seçimlik. Evet sdl denetimi açıkken anında warning ve error aldım.

Daha da ilginci, debug mode ve release mode denemelerimde farklı sonuçlar aldım.

Yukarıdaki debug mode için,

Aşağıdaki release mode için.

Yani iki türlü de sdl kontrolü yapmadan kodu çalıştırabildim.

Sadece tahminde bulunuyorum. Debug mode bir bellek alanı tahsis edip simüle ediyor ve o bellek alanında hangi veri varsa onu döndürüyor.

Release modda işletim sistemi isteği sıfır ile geri çeviyor.

Tabi tartışmaya açık.

Bu aynı mesajın 4. yada 5. editi uzadı başını bulamıyorum.

Okudukları denediklerim fikrimi değiştirmedi. C bu tür kodlamaya gördüğüm kadarıyla hala izin veriyor. Ekrem beyin dediği gibi de PC kullanıcıları için gelişmiş kontrol seçenekleri ile de hata olarak da uyarı olarak da değerlendirme seçenekleri mevcut.

Yani bu konuda herhangi bir IDE beni derlerken uyarmazsa kızmam.

Başta söylediğim gibi soru sahibinin asıl sorunu, while koşulu asla oluşmayacak bir koşul olduğu için while koşuluna asla ulaşamazsın gibi bir hata almaması normal bu bir mantık hatası ve bununla çoğunlukla derleyiciler ilgilenmez. (istisnaları ve çalışmalar mevcut)

Çözümü koşulu doğru oluşturmak koşulu doğru oluşturursanız while döngüsüne girer ve kod çalışır.

Ama itirazın olduğu nokta, nasıl ilk atama yapılmamış bir değişken yüzünden zaten derlenememesi uyarı alman gerekirdi kısmınaydı sanırım.

O kısımı da dilimin döndüğünce anlattım. Bence doğal vermeyebilir, çünkü C buna cevaz veriyor. Derleyicilerin kimi de bu konuda opsiyonlar içeriyor, dilin standardı var derleyicilerin yok. Çok farklı derleyiciler kullandım aynı kod için çok farklı davranışlar seyrettikleri gibi çok farklı standart dışı özellikler sergilediklerine de şahit oldum.

Tabi ki Ekrem hocamın bu konudaki görüşlerini de merakla bekliyorum.

Umarım birilerine faydası olur.

Bu gün üzerinde çalıştığım konuda bu mesajım son olsun. Editlemek ve eklemek yapmak yoruyor.

Konu buradan çıktı.

Evet şimdi verdiğiniz linki okumuş olarak cevap verebilirim. Evet çalışması mümkün. Eğer gönderilen değer, 1 olursa belki. Bu durumda veriyi integer tanımladığımızı düşünürsek, integer için makineye göre değişmekle beraber eğer buraya takılırsanız sizeof() ile buna da bakabiliriz,

-2147483648 to 2147483647 aralığında neredeyse dört milyarda bir ihtimal. Tabi bu ihtimale bellek bölgesinde gelecek değerlerin değişkenliğini nasıl ekleriz istatistiki olarak orasına bakmadım dahi.

Bir şey mi atladım diye bakıyorum, C11 dahil C++ değil komite c++ da 2017 tadilat yapmış bunun dışında bu uygulamayı kural ihlali olarak görmüyor.

Evet undefined behavior konusunda bir çok kaynaktan tekrar tekrar baktım, bunu bir hata gibi görenlerin yaklaşımlarını da okudum. Karşıt görüşleri de okudum.

Hatta burada sizin fikrinizde olan ve olmayan güzel metinler var onu da buraya bırakmak isterim.
Undefined behavior in C is a reading error. – keeping simple (yodaiken.com)

Adamlar veryansın ediyor komiteye nasıl bunu hata kabul etmezsiniz diye. Tabi doğal bulanlar da var.

Farklı derleyiciler ile değer atanmamış olmasına rağmen, bir uyarı alamadığım gibi, ne değer verdiğine bakılmaksızın while doğru kurgulandığında kod çalışıyor, istisnası, koşula eşdeğer rastgele bir bellek değeri gelmesi bu da az önce açıkladığım üzere neredeyse dört milyarda bir bir ihtimal.

Kod yanlış burada göreceli bir kavram. Size göre yanlış. Çünkü siz ilk değer verilmediğinden öngörülemeyen bir değer alıp hatalı çalışabileceği düşüncesindesiniz.

Ben ise C ye göre bu durum yanlış değil, nasıl olsa while döngüsünde if koşulu sağlandığında ilk değerini alacak o zamana kadar bellekte yer kaplamasa da olur bunu bir optimizasyon olarak görebilirim, risklidir ama olası bir yöntemdir diye bakıyorum.

Evet denedim tabi ki risklidir ve en başından zaten ilk değer atamak tavsiyem bu bence detay olan riskleri elimine etmekti.

Ama şurada anlaşamadık. İlk değer de versek de vermesekte while koşulunu sağlayıp döngüyü sağlaması için ayrıca değerin bir yerde verilmesi gerekli. İlk değer 0 versem dahi, while koşulu 1 durumda çalışacak şekilde kodlanmışsa ve bir yerlerde değeri 1 verilmediği sürece kod çalışmayacak burası zaten mantık hatası olan kısım.

Üşenmedim yeni nesil bir derleyici daha indirdim.

Bu Rad Studio C derleyicisi.

Varsayılan ayarlada sorgusuz çalıştı.

Çünkü bu C diline göre hata değil, sorumluluğu kullanıcıda olan bir kod. Bu nedenle asla yanlış diyemem.

Zaten bir çok derleyici de yanlış demiyor.

Standart da yanlış demiyor. Hata demiyor. Dikkat et seviyesinde kullanıcıya bırakmakta direniyorlar.

Size göre bunlar yazım hatası ile eş değer olabilir saygı duyarım ama, dile ve editöre göre bunlar bir hata olarak kabul edilmiyor.

Elimde de eski yeni ne kadar derleyici var ise denedim. Microsfot VS ilginç bir şekilde SDL altyapısı kullandığı ve çapraz platform için çalışırken kullandığı yapıda ancak bunu bir hata olarak yakalıyor.

Gcc eski sürümlerinde de bir problem görmedim. Ancak yeni sürümlerde belki özellikle kontrol edilirse bu hata olarak tanımlanabilir.

Bu kadar üzerinde durduğum konu bu idi. Hangi ayarlarmış diye baktım. C dili standart. C dili kesinlikle ön tanımlama istemiyor. Standart C derleyicilerinin hiç biri de bunu hata olarak vermiyor. Zaten konudaki itirazım da bu. Bunu bir hata olarak yakalamak ancak debug ayarları ile mümkün.

Bu mesajda yazdığım mesajda bu konuda tartışılan linki verdim. Evet bundan şikayetçiler ve nedeni de standart neden bunu zorunlu tutmuyor diye yazmışlar. Tutmuyor yapabileceğim bir şey yok. Tutmuyor o kadar. Ben nedenini anlıyorum.

C nin ruhu bu, tip kontrolsüz, sınır kontrolsüz, erişim kontrolsüz, sebepleri bana mantıklı geliyor. Basit derleyici tasarlayabilmek, extern ifadelere yer verebilmek, dinamik bellek yönetimi kabiliyeti bulunmak gibi bir çok mantıklı sebep görebiliyorum.

Zaten bu şekilde olduğu için C de decleration ve defination tanımları ayrıdır.

Neyden bahsediyorsun dediğiniz kısma bundan bahsediyorum işte.

Ciddi ciddi vakit ayırdım. Sizin verdiklerinizi okudum, standartlara göz gezdirdim, birden çok derleyici ile de denedim. Görüldüğü üzere buna hata olarak bir derleyici cevap vermez vermiyorda.

İstediğiniz denemem gereken başka derleyici varsa onlarda da bakabiliriz.

Benim edindiğim izlenim, C ve C destekli bir çok derleyici bunları hata kabul etmiyor. Bu nedenle uyarı dahi vermiyor.

Evet milyonlarca satır kodda böyle bir risk ciddi problem ama, yukarıdaki örneğin çalışmamasında asıl suçlu ilk değer atanmaması değil bence.

C hakkında konuşuyor isek zaten şikayet ettikleri neden bunlar hata olarak yönetilmiyor. Evet yönetilmiyor bence de yönetilmesine gerek yok. Bunları yönetmek isteyenler zaten, tip kontrollü dilleri kullanabilirler ki öyle yapıyorlar.

2 Beğeni