İçeriğe geç
academia.sh

Ders 05 / 13

Derleyici Uyarıları ve Standartlar

Bir uyarı yalnız derleyici aşamasında doğabilir, çünkü yalnız o aşama tek bir birimin içindeki tipleri ve ifadeleri görür. Standardın garanti ettiği ile uygulamaya bıraktığı yan yana konuyor — sizeof(char) == 1 garantidir, öteki boyutlar değildir. Uyarısız derlenen bir programın doğru olduğu da söylenemez; bunu göstermek için uyarı basmayan ama yanlış bir sonuç üreten bir hesap koşturuluyor.

Yayın Güncelleme

Bu derste

  • Bir uyarının neden yalnız derleyici aşamasında doğabileceğini gerekçelendirir
  • Standardın garanti ettiği ile uygulamaya bıraktığı davranışı bir örnekle ayırt eder
  • Uyarısız derlenmenin doğruluk garantisi olmadığını çalışan bir örnekle gösterir
İçindekiler

Önceki ders ön işlemcinin dört kararını derinleştirdi ve kapanışta bir gözlemi öne çıkardı: bu dört ders boyunca hiçbir kod bloğu bir uyarı bırakmadı, ama bu tesadüf değil, elle sağlanan bir tutarlılıktı. Bu ders o tutarlılığın nasıl denetlendiğine bakıyor — bir uyarının hangi aşamada doğduğuna ve standardın neyi garanti edip neyi uygulamaya bıraktığına. Kurs, açılışında dört aşamayı adlandırmıştı; bu son ders o dört adı bir kez daha kullanıyor, ama bu kez bir “karar” değil bir “denetim” için: dört aşamadan yalnız biri, kendi girdisinin sınırları içinde kalarak, kaynağın muhtemelen yanlış olduğunu düşünebiliyor. Bu dersin ölçtüğü, o düşüncenin doğduğu yer ve o düşüncenin ne kadar güvenilir olduğudur.

Uyarı Nerede Doğar

Bir uyarı yalnız derleyici aşamasında doğabilir. Ön işlemci tip bilmediği için bir tipin yanlış kullanıldığını fark edemez — onun elinde yalnız metin var. Bağlayıcı yalnız adları eşler, bir ifadenin içeriğini hiç incelemez. Koşum ise kaynağın kendisini artık taşımıyor; elindeki yalnız veri, uyaracağı bir şey yok. Derleyici ise tam olarak bunun tersini yapıyor — tek bir birimin içindeki her ifadenin tipini, her değişkenin kullanımını, her denetim yolunun hangi değeri döndürdüğünü görüyor ve bu görüşün sonucunda bir şeyin şüpheli olduğunu düşünebiliyor.

Bu, Derleme Aşamaları dersindeki gözlemin bir uzantısı: dört aşamanın her biri yalnız kendi girdisine bakıyordu ve bir hatanın nerede yakalanacağı da bu girdiyle sınırlıydı. Uyarı, tam olarak derleyicinin kendi girdisinin sınırları içinde kalan bir denetimdir — yalnız tek bir birime bakarak fark edebileceği bir şeyi işaretler. Bir hata birden çok birim arasında yayılıyorsa (Başlık Dosyaları dersindeki bildirim/tanım uyuşmazlığı gibi), derleyicinin bunu bir uyarıyla yakalaması da mümkün değildir — çünkü o hata, derleyicinin girdisinin tamamen dışında kalır.

Şüpheli olmak yanlış olmakla aynı şey değildir. Bir uyarı tipik olarak şu kalıplardan birine işaret eder: bir sabit değerin taşıyamayacağı kadar dar bir tipe doğrudan atandığı, bir değişkenin tanımlandığı ama hiç kullanılmadığı, işaretli bir sayıyla işaretsiz bir sayının doğrudan karşılaştırıldığı, ya da bir işlevin döndürmesi beklenen bir değeri bazı yollarda hiç döndürmediği. Dördü de aynı özelliği taşır: kaynak derlenebilir durumdadır, ama derleyici, tek bir birimin içindeki tipleri ve akışı görerek, kendi gördüğü kalıptan bir risk çıkarmıştır.

Bu dört kalıbın ortak noktası, hiçbirinin kendi başına bir hata olmamasıdır. Kullanılmayan bir değişken kaynağın çalışmasını engellemez; işaretli bir sayıyla işaretsiz bir sayının karşılaştırılması geçerli bir C ifadesidir; bir işlevin bazı yollarda değer döndürmemesi kaynağı derlenemez kılmaz. Derleyici bu kalıpların hiçbirini reddetmiyor, yalnız işaretliyor — çünkü her biri, programcının aslında istemediği bir şeyin belirtisi olabilir, ama olmak zorunda da değildir. Bu, uyarının neden isteğe bağlı bir bayrakla (-Wall, -Wextra gibi) açılması gerektiğini de açıklıyor: standart, derleyicinin hangi kalıpları şüpheli sayacağını belirlemez, yalnız programın hangi kalıplarla derlenebilir kalacağını belirler.

Bu kursun bütün kod örnekleri bu dört kalıptan kaçındı, ama kaçınmak elle yapılan bir seçimdi — dilin kendisi hiçbir örneği bu kalıplara girmekten alıkoymadı. Bu konudaki yirmi bir kod bloğunun hepsi uyarısız kaldıysa, bunun nedeni derleyicinin cömert davranması değil, her değişkenin kullanılması, her karşılaştırmanın aynı işaretteki tiplerle yapılması, her işlevin her yolda bir değer döndürmesiydi.

Bu tutarlılığı sağlamak, kursun kalan bölümünde de aynı yükümlülük olarak kalacak. Bir uyarı çıkardığında, o uyarı kaynaktaki tek bir birime, tek bir ifadeye işaret eder — kaynağı düzeltmenin ilk adımı, uyarının hangi birime ait olduğunu bilmektir. Bu da uyarının neden derleyici sınıfına bu kadar sıkı bağlı olduğunu bir kez daha doğruluyor: bir uyarı, ancak onu doğuran birim yeniden derlenince kaybolur; başka hiçbir aşamanın onu susturmaya gücü yetmez.

Ne Garanti, Ne Değil

C standardı (C17) bazı şeyleri kesin olarak garanti eder, bazılarını uygulamaya bırakır. Aradaki fark koşturarak gösterilebilir — ama yalnız garanti edilen tarafı, çünkü garanti edilmeyen bir sayı bu kursun hiçbir yerinde basılmaz.

Bu ayrım rastgele değil, dilin donanıma yakınlığının doğrudan sonucu. Dört ayrı donanım dört ayrı tam sayı genişliği sunabilir; standart, dilin her donanımda çalışabilmesi için kesin bir genişlik dayatmak yerine bir alt sınır dayatıyor ve gerisini derleyiciye, dolayısıyla dolaylı olarak donanıma bırakıyor. intin bu makinede kaç bayt olduğu bu yüzden dilin bir sözü değil, derleyicinin bir kararıdır — ve bu kurs boyunca hiçbir yerde basılmayan bir gözlemdir.

// garanti.c — standardin garanti ettigi ile birakmadigi yan yana
#include <limits.h>
#include <stdio.h>

int main(void) {
    printf("sizeof(char) == 1: %s\n", (sizeof(char) == 1) ? "dogru" : "yanlis");
    printf("CHAR_BIT >= 8: %s\n", (CHAR_BIT >= 8) ? "dogru" : "yanlis");
    printf("INT_MAX >= 32767: %s\n", (INT_MAX >= 32767) ? "dogru" : "yanlis");
    printf("LONG_MAX >= 2147483647: %s\n", (LONG_MAX >= 2147483647L) ? "dogru" : "yanlis");
    return 0;
}
sizeof(char) == 1: dogru
CHAR_BIT >= 8: dogru
INT_MAX >= 32767: dogru
LONG_MAX >= 2147483647: dogru

DD13. Dört satırın dördü de bir karşılaştırmanın sonucunu basıyor, hiçbiri ham bir sayıyı basmıyor. sizeof(char), CHAR_BIT, INT_MAX, LONG_MAXin kendi değerleri uygulamaya göre değişebilir — standart yalnız bir alt sınır garanti eder, üst sınırı serbest bırakır. intin gerçekte kaç bayt olduğu, longun gerçekte kaç bit taşıdığı bu kursun hiçbir yerinde basılmadı ve basılmayacak; basılan yalnız “bu değer en az şu kadardır” sorusunun dogru/yanlis yanıtı, çünkü bu yanıt standarda uygun her C17 derleyicisinde aynıdır.

Bu dört satırın hepsi dogru çıktı, çünkü hepsi gerçekten garanti edilen alt sınırlar. Standardın garanti etmediği şey ise satırlarda hiç görünmüyor: sizeof(int)in tam değeri, intin shorttan gerçekten daha geniş olup olmadığı, charın imzasız mı imzalı mı sayıldığı. Bunların hepsi uygulamaya bırakılmıştır ve bu yüzden bu kursun hiçbir dersinde koşturulmadı.

Alt sınır ile üst sınır arasındaki bu fark, kursun daha önce gördüğü “garanti ile kesit” ayrımına benziyor: bir başlık dosyası bir bildirimi paylaşabiliyordu, ama tanımın onunla uyuşacağını garanti etmiyordu (Başlık Dosyaları dersi); burada da standart bir alt sınır veriyor, ama gerçek değerin o sınırın neresinde duracağını garanti etmiyor. İkisinde de aynı örüntü tekrar ediyor: dilin verdiği söz her zaman bir asgaridir, üstüne ne eklendiği uygulamaya, yani derleyiciye ve donanıma kalır.

Bu dört satırın hiçbiri bir uyarı da doğurmadı ve bu tesadüf değil. CHAR_BIT >= 8 gibi bir karşılaştırma, iki sabit ifadenin karşılaştırılmasıdır ve derleyici bunu şüpheli bulacak bir kalıp görmüyor — karşılaştırmanın iki tarafı da aynı geniş tam sayı sınıfına ait, işaretli ile işaretsiz arasında bir karışıklık yok, bir değer daralmıyor. Standardın garanti ettiği tarafı ölçmek bu yüzden hem doğru hem güvenli: ne ortama bağlı bir sayı sızdırıyor, ne de derleyicinin şüphelendiği bir kalıba giriyor.

Bu ayrımın pratik bir sonucu var: bir kaynak, yalnız standardın garanti ettiği taraf üzerine kurulduğunda, hangi derleyiciyle ve hangi donanımla derlendiğinden bağımsız olarak aynı sonucu verir. sizeof(char) == 1 her yerde dogrudur, çünkü standart bunu zaten zorunlu kılmıştır. Ama bir kaynak sizeof(int) == 4 gibi bir varsayıma dayansaydı, bu varsayım bazı uygulamalarda doğru, bazılarında yanlış çıkabilirdi — ve derleyici bu varsayımı kaynakta göremeyeceği için, yanlış çıktığı yerde de bir uyarı basmazdı. Yalnız garanti edilen tarafa dayanan bir kaynak, hangi derleyiciyle ve hangi donanımla derlendiğinden bağımsız olarak doğruluğunu korur; garanti edilmeyen bir tarafa dayanan bir kaynak ise, derleyici hiçbir uyarı basmasa bile, farklı bir donanımda sessizce yanlış sonuç verebilir.

Sınırlayıcı Ölçüm

Bir uyarı bir hata değildir; derleyici uyarı bastıktan sonra bile programı derlemeye devam eder ve program çalışır. Bunun aynadaki yansıması da doğrudur: uyarısız derlenen bir programın doğru olduğu söylenemez. Bu iki cümle birlikte, uyarının ne olduğunu değil, ne olmadığını tanımlıyor: bir uyarı ne bir hata kanıtıdır, ne yokluğu bir doğruluk kanıtıdır — yalnız derleyicinin fark ettiği şeyler hakkında bir bilgidir, o kadar. Aşağıdaki program hiçbir uyarı bırakmadan derlenir, ama ikinci satırı beklenmeyen bir sonuç verir.

// oran.c — uyarisiz derlenen bir program dogru oldugunu garanti etmez
#include <stdio.h>

int main(void) {
    int parca = 2;
    int toplam = 5;

    int tamsayi_oran = parca / toplam * 100;
    double kayan_oran = (double)parca / toplam * 100;

    printf("tam sayiyla hesaplanan oran: %d\n", tamsayi_oran);
    printf("kayan noktayla hesaplanan oran: %.1f\n", kayan_oran);

    return 0;
}
tam sayiyla hesaplanan oran: 0
kayan noktayla hesaplanan oran: 40.0

DD14. İki satır da aynı iki sayıdan (2 ve 5) yola çıkıyor ve ikisi de derleyiciden tek bir uyarı almadı. Ama tamsayi_oran 0 verdi, çünkü parca / toplam iki int arasında yapıldı ve 2 / 5 sıfıra doğru yuvarlanarak 0 verdi — çarpma 100le ondan sonra geldi ve zaten sıfır olan bir sayıyı yüz katına çıkarmak yine sıfır verdi. kayan_oran ise parcayı önce doublea çevirdiği için bölme kayan noktalı aritmetikte yapıldı ve doğru oranı, 40.0ı verdi. Derleyici bu ikisinden hiçbirini “yanlış” bulmuyor, çünkü ikisi de kendi tipiyle tutarlı: int / int işleminin sonucunun int olması C’nin bir kuralı, bir hata değil — bu kursun ikinci dersinde 7 / 2 ile 7 / 2.0 arasındaki fark olarak zaten görüldü.

Kaynakta tamsayi_oranı düzeltmenin yolu, tıpkı kayan_oranda olduğu gibi, bölmeden önce taraflardan birini doublea çevirmektir — programın çalıştırdığı işlem aynı kalır, yalnız hangi aritmetiğin kullanılacağı kaynakta açıkça belirtilir. Derleyici bu düzeltmeyi öneremez, çünkü önermek için “programcı burada bir oran istiyordu” bilgisine ihtiyaç duyar ve bu bilgi hiçbir yerde yazılı değildir. Derleyicinin görebildiği tek şey, iki ifadenin de kendi tipiyle tutarlı olduğudur.

Bu, dilin verdiği bir güvenceyle programcının verdiği bir kararı birbirinden ayırıyor. Dil int / intin int vereceğini garanti ediyor ve bunu tutarlı biçimde uyguluyor — derleyici burada hiçbir şeyi ihlal etmiyor. Yanlış olan, iki tam sayının bölümünün bir oran taşıyacağını varsaymak; bu varsayım kaynakta hiçbir yerde yazılı değil, yalnız programcının zihnindeydi. Derleyici zihinde olanı göremez, yalnız kaynakta yazılanı görür — ve kaynakta yazılan, tamamen tutarlı, tamamen uyarısız bir int bölmesiydi.

Bu, dört ders boyunca kurulan bir örüntünün son halkası: her aşama yalnız kendi girdisine bakar ve kendi girdisinde tutarlı olan her şeyi kabul eder. Ön işlemci metni tutarlı buldu, derleyici tipleri tutarlı buldu — ama tutarlılık doğrulukla aynı şey değildir. tamsayi_oran değişkeni derleyicinin gözünden kusursuzdur; bir programcının gözünden ise muhtemelen bir hatadır. Bu ikisi arasındaki boşluk, hiçbir aşamanın dolduramayacağı bir boşluktur — onu yalnızca kaynağı yazan doldurabilir.

Bu, beş ders boyunca kurulan çerçevenin son dersi için de doğru olan bir sonuç: dört aşamanın hiçbiri, kaynağın doğru olduğunu kanıtlamaz — yalnız kendi aşamasına özgü bir tutarlılığı denetler. Ön işlemci metnin biçimini denetler, derleyici tipleri ve akışı denetler, bağlayıcı adları denetler, koşum ise yalnız verdiği veriyle çalışır. Dördü birden geçildiğinde elde edilen şey, çalışan bir programdır — ama çalışan bir programın istenen programla aynı olduğunu söyleyen hiçbir aşama yoktur.

Özet

  • Bir uyarı yalnız derleyici aşamasında doğabilir; yalnız bu aşama tek bir birimin içindeki tipleri ve ifadeleri görüp bir kalıbı şüpheli bulabilir.
  • Bir uyarı bir hatayla aynı şey değildir: program uyarıyla birlikte de derlenir ve çalışır.
  • Standart bazı şeyleri kesin garanti eder (sizeof(char) == 1, CHAR_BIT >= 8), bazılarını uygulamaya bırakır (gerçek tip boyutları); bu kurs yalnız garanti edileni bastı.
  • Uyarısız derlenen bir program doğru olduğunu göstermez: int / int bölmesi dilin bir kuralıdır, ama sonucu bir oran sanmak programcının kaynakta yazmadığı bir varsayımdır.
  • Standardın verdiği söz her zaman bir asgaridir; üstüne ne eklendiği derleyiciye ve donanıma kalır, bu yüzden bu kurs boyunca yalnız asgari, garanti edilen taraf koşturuldu.
  • Dört aşamanın hiçbiri kaynağın doğru olduğunu kanıtlamaz; her biri yalnız kendi aşamasına özgü bir tutarlılığı denetler, çalışan bir programla istenen program arasındaki farkı yakalamak dilin değil kaynağı yazanın işidir.

Sonraki Adım

Bu, Dil ve Derleme konusunun son dersidir. Beş ders aynı çekirdekten, aynı dört sınıftan geçti ve her biri ayrı bir köşeyi aydınlattı. C’nin Konumu çekirdeği tam koşturdu ve on dört gözlemin 4/4/3/3 dağılımıyla dört aşamanın adını koydu. Derleme Aşamaları aynı dört sınıfı küçük bir kesitle yan yana koşturup her birinin hangi girdiye baktığını gösterdi ve aşamaların geri dönüşü olmayan bir sırayla çalıştığını kanıtladı. Başlık Dosyaları bildirim ile tanımı ayırdı ve bir başlığın aynı bildirimi birden çok birime nasıl taşıdığını, çifte içerme korumasının neyi engellediğini gösterdi. Ön İşlemci dört ayrı yönergenin aslında tek bir işin — metin ikamesinin — görünümü olduğunu ortaya koydu ve bir makronun argümanını birden çok kez yerine koyabildiğini kanıtladı. Bu son ders beşinci köşeye baktı: bir uyarının yalnız derleyicide doğabildiğini ve uyarısız derlenmenin doğruluk garantisi olmadığını.

Beş dersin ortak paydası, hiçbirinin öbürünün ölçtüğü şeyi tekrar ölçmemesiydi — her biri aynı dört adı kullandı, ama her biri kendi sorusunu sordu. Bu ortak payda kursun geri kalanında da sürecek: Temel Sözdizim konusunun sekiz dersi de aynı dört sınıfı kullanacak, ama her biri C’nin farklı bir köşesine — tiplere, değişkenlere, işleçlere, denetim akışına, fonksiyonlara, dizilere, kayıt tiplerine, girdi ve çıktıya — bakacak.

Temel Sözdizim konusu bu çerçeveyi değiştirmiyor, ona C’nin kendi tiplerini, değişkenlerini ve işleçlerini ekliyor; ilk soru, bir tipin taşıdığı değerlerin hangi aşamada karara bağlandığıdır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat