D == 0 iken neden 'd / = d' sıfır istisna ile bölme atmıyor?


81

Neden sıfır istisna ile bölme yapmadığımı tam olarak anlamıyorum:

int d = 0;
d /= d;

Bunun yerine sıfır istisna ile bir bölme almayı bekliyordum d == 1.

Neden d /= dsıfır istisna ile bölme atmıyor d == 0?


25
Bu tanımlanmamış bir davranış.
LF

51
Sıfır istisna ile bölme diye bir şey yoktur.
πάντα ῥεῖ

15
Bazı açıklamaları açıklığa kavuşturmak için: "sıfır istisnaya göre bölme" hakkında bir mesaj gördüğünüzde bu, işletim sisteminin size bir şeylerin ters gittiğini söyleyenidir. Öyle değil bir C ++ istisna. C ++ 'da, istisnalar bir throwifade tarafından atılır . Başka bir şey yok (tanımlanmamış davranış ülkesinde değilseniz).
Pete Becker

9
C ++ 'da " sıfır istisnaya bölme " diye bir şey yoktur .
Algirdas Preidžius

6
@ user11659763 "Bu yüzden tanımlanmamış bir davranış: tamamen hedefe bağlı." - Bu davranış aracı tanımlanmamış bu değil hiç ; tanımladığınız şey, uygulama tanımlı davranıştır . Tanımlanmamış davranış çok çok daha güçlü bir ifadedir.
marcelm

Yanıtlar:


108

C ++ 'da yakalanacak bir "Sıfıra Göre Bölme" İstisnası yoktur. Gözlemlediğiniz davranış, Derleyici optimizasyonlarının sonucudur:

  1. Derleyici Tanımsız Davranışın olmadığını varsayar
  2. C ++ 'da Sıfıra göre bölme tanımsız bir davranıştır
  3. Bu nedenle, kod olabilir sıfıra bölmeye yol bunu değil tahmin ediliyor.
    • Ve Sıfıra Bölünmeye neden olması gereken kodun asla gerçekleşmeyeceği varsayılır.
  4. Bu nedenle, derleyici, Tanımsız Davranış gerçekleşmediğinden, bu koddaki ( d == 0) Tanımsız Davranış koşullarının gerçekleşmemesi gerektiğini çıkarır.
  5. Bu nedenle, d / dher zaman 1'e eşit olmalıdır.

Ancak...

Derleyiciyi kodunuzda küçük bir değişiklik yaparak sıfıra "gerçek" bir bölümü tetiklemeye zorlayabiliriz.

volatile int d = 0;
d /= d; //What happens?

Öyleyse şimdi soru kalıyor: derleyiciyi temelde bunun olmasına izin vermeye zorladığımıza göre, ne olacak? Bu tanımlanmamış bir davranıştır - ancak şimdi derleyicinin bu tanımsız davranış etrafında optimizasyon yapmasını engelledik.

Çoğunlukla hedef ortama bağlıdır. Bu yazılım istisna tetiklemez ama olabilir bir Donanım İstisna tetikleyebilir (hedef CPU bağlı olarak) (Tamsayı-Böl-by-Zero) bir yazılım durum yakalandı edilebilir geleneksel tarzda yakalanmış olamaz. Bu kesinlikle bir x86 CPU ve diğer (ama hepsi değil!) Mimariler için geçerlidir.

Bununla birlikte, sadece programın çökmesine izin vermek yerine donanım istisnasıyla başa çıkmanın yöntemleri vardır (eğer meydana gelirse): uygulanabilir olabilecek bazı yöntemler için bu gönderiye bakın : İstisnayı yakalama: sıfıra böl . Derleyiciden derleyiciye değiştiklerini unutmayın.


25
@Adrian Her ikisi de davranış tanımsız olduğu için mükemmel bir şekilde tamam. Kelimenin tam anlamıyla her şey yolunda.
Jesper Juhl

9
"C ++ 'da Sıfıra Göre Bölme tanımlanmamış bir davranıştır" -> derleyicinin bu optimizasyonu IEE754 altındaki kayan nokta türleri için yapamayacağına dikkat edin. D'yi NaN olarak ayarlamalıdır.
Bathsheba

6
@RichardHodges, IEEE754 altında çalışan bir derleyicinin ikili optimizasyon yapmasına izin verilmez: NaN üretilmelidir.
Bathsheba

2
@formerlyknownas: Bu bir "UB'yi optimize etmek" meselesi değil - hala "her şey olabilir" durumudur; sadece üretmek 1tamamen geçerli bir şeydir. 14684554'ü elde etmek, derleyicinin daha da optimize ettiği için olmalıdır - başlangıç d==0koşulunu yayar ve bu nedenle yalnızca "bu 1 veya UB'dir" değil, aslında "bu UB, nokta" sonucuna varabilir. Bu nedenle sabiti yükleyen kod üretme zahmetine bile girmez 1.
hmakholm,

1
İnsanlar her zaman optimizasyonu önlemek için geçici öneriyorlar, ancak değişken bir okuma veya yazmayı oluşturan şey uygulama tanımlıdır.
philipxy

38

Sırf diğer cevapları tamamlamak için, sıfıra bölmenin tanımsız bir davranış olduğu gerçeği , derleyicinin, olacağı durumlarda her şeyi yapmakta özgür olduğu anlamına gelir :

  • Derleyici bunu varsayabilir 0 / 0 == 1ve buna göre optimize edebilir . Burada yaptığı etkili bir şekilde bu.
  • Derleyici, eğer isterse, bunu varsayabilir 0 / 0 == 42ve dbu değere ayarlayabilir .
  • Derleyici ayrıca değerinin dbelirsiz olduğuna karar verebilir ve böylece değişkeni başlatılmamış olarak bırakabilir, böylece değeri, kendisi için ayrılmış belleğe önceden yazılan her şey olur. Yorumlarda diğer derleyicilerde gözlemlenen beklenmedik değerlerden bazıları, bu derleyicilerin böyle bir şey yapmasından kaynaklanıyor olabilir.
  • Derleyici ayrıca sıfıra bölme gerçekleştiğinde programı iptal etmeye veya bir istisna oluşturmaya karar verebilir. Bu program için derleyici bunun her zaman olacağını belirleyebildiğinden, istisnayı yükseltmek (veya yürütmeyi tamamen iptal etmek) için kodu yayınlayabilir ve işlevin geri kalanını erişilemez kod olarak değerlendirebilir.
  • Sıfıra bölme oluştuğunda bir istisna ortaya çıkarmak yerine, derleyici programı durdurup bunun yerine bir Solitaire oyunu başlatmayı da seçebilir. Bu da "tanımlanmamış davranış" şemsiyesi altına giriyor.
  • Prensip olarak, derleyici, sıfıra bölünme gerçekleştiğinde bilgisayarın patlamasına neden olan bir kod bile verebilir . C ++ standardında bunu yasaklayacak hiçbir şey yoktur. (Bir füze uçuş kontrolörü gibi belirli türden uygulamalar için bu, istenen bir güvenlik özelliği olarak bile düşünülebilir!)
  • Ayrıca standart , tanımlanmamış davranışların "zamanda yolculuk" yapmasına açıkça izin verir , böylece derleyici, sıfıra bölme gerçekleşmeden önce yukarıdaki şeylerden herhangi birini (veya başka herhangi bir şeyi) yapabilir . Temel olarak standart, programın gözlemlenebilir davranışı değişmediği sürece derleyicinin işlemleri serbestçe yeniden düzenlemesine izin verir - ancak programın yürütülmesi tanımsız davranışa neden olacaksa bu son gereksinim bile açıkça feragat edilir. Yani, aslında, tüm herhangi bir program yürütme davranışı, bir noktada, tetik tanımsız davranış tanımlanmamış olacağını!
  • Yukarıdakilerin bir sonucu olarak, derleyici, tanımsız davranışın olmadığını da varsayabilir , çünkü bazı girdiler üzerinde tanımsız bir şekilde davranan bir program için izin verilen davranışlardan biri , girdinin bir şeymiş gibi davranmasıdır . başka . Yani, orijinal değeri dderleme zamanında bilinmese bile , derleyici yine de hiçbir zaman sıfır olmadığını varsayabilir ve kodu buna göre optimize edebilir. OP'ın kodunun özel durumda, bu sadece varsayarak derleyici etkin bir farksızdır 0 / 0 == 1, ama derleyici de, örneğin, varsayalım olabilir puts()de if (d == 0) puts("About to divide by zero!"); d /= d;asla çalıştırılmaktadır!

29

Tamsayı sıfıra bölme davranışı C ++ standardı tarafından tanımlanmamıştır. O edilir değil bir özel durum için gerekli.

(Kayan noktayı sıfıra bölme de tanımsızdır, ancak IEEE754 bunu tanımlar.)

Derleyiciniz için optimize d /= dediyor, d = 1bu da makul bir seçimdir. Kodunuzda tanımsız bir davranış olmadığını varsaymasına izin verildiğinden bu optimizasyonu yapmaya izin verilir - bu dmuhtemelen sıfır olamaz.


3
Ekstra net olmak önemlidir, başka bir şey de olabilir, IOW bu davranışa güvenilemez.
hyde

2
Derleyicinin "bu dmuhtemelen sıfır olamaz " olduğunu varsaymasının makul olduğunu söylediğinizde, derleyicinin şu satırı görmediğini de varsayıyor musunuz: int d = 0;?? :)
Adrian Mole

6
Derleyici görüyor, ancak muhtemelen umursamıyor. Bunun gibi uç bir durum için zaten çılgınca olan karmaşık derleyicide gereken ekstra kod karmaşıklığı muhtemelen buna değmez.
user4581301

1
@ user4581301 Her ikisinin birlikte alınması, zehirli bir dalı algılamasına ve çok daha fazla kodu budamasına izin verir. Bu yüzden faydalı olacaktır.
Deduplicator

3
Dolayısıyla, "int d = 0; if (d == 0) printf (" d = sıfır \ n "); d / = d;" yazdıysanız, derleyici printf'i de kaldırabilir.
gnasher729

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.