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 / intbö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.