Tanımsız Davranışın Arkasındaki Felsefe


59

C \ C ++ özellikleri, derleyicilerin kendi yöntemleriyle uygulayabilecekleri çok sayıda davranış ortaya koyar. Burada hep aynı soruyu sormaya devam eden birkaç soru var ve bu konuda bazı mükemmel yayınlarımız var:

Benim sorum tanımsız davranışın ne olduğu ile ilgili değil ya da gerçekten kötü. Tehlikeleri ve ilgili tanımsız davranış tekliflerinin çoğunu standarttan biliyorum, bu yüzden ne kadar kötü olduğu konusunda cevaplar göndermekten kaçının. Bu soru, derleyici uygulaması için pek çok davranışı açık bırakmanın ardındaki felsefe ile ilgilidir.

Performansın asıl sebep olduğunu belirten mükemmel bir blog yazısı okudum . Performansın izin vermenin tek kriteri olup olmadığını merak ediyordum ya da işleri derleyici uygulaması için açık bırakma kararını etkileyen başka faktörler var mı?

Belirli bir tanımsız davranışın, derleyici için en iyi duruma getirme için yeterli alan sağladığından bahseden herhangi bir örneğiniz varsa, lütfen bunları listeleyin. Performans dışında başka herhangi bir faktör olduğunu biliyorsanız, lütfen cevabınızı yeterli ayrıntı ile geri gönderin.

Soruyu anlamadıysanız veya cevabınızı destekleyecek yeterli delil / kaynak yoksa, lütfen geniş kapsamlı spekülatif cevaplar göndermeyin.


7
Zaten deterministic bilgisayarı kim duydu?
sova

1
litb'in mükemmel cevabı programmers.stackexchange.com/a/99741/192238 tarafından belirtildiği gibi, bu sorunun başlığı ve gövdesi biraz uyuşmuyor gibi görünüyor: "derleyicilerin kendi yöntemleriyle uygulamalarına açık davranışlar" genellikle uygulama tarafından tanımlanır . elbette, gerçek UB'nin uygulama yazarı tarafından tanımlanmasına izin verilir, ancak çoğu zaman rahatsız etmezler (ve hepsini optimize ederler vb.)
underscore_d

Yanıtlar:


49

Öncelikle, burada sadece "C" den bahsetmeme rağmen, aynı şeyin C ++ için de aynı şekilde geçerli olduğunu not edeceğim.

Godel'den bahseden yorum, kısmen (ancak yalnızca kısmen) noktasındaydı.

Buna aşağı olsun, C standartlarında tanımsız davranış olduğunu büyük ölçüde sadece standart girişimleri tanımlamak ne arasındaki sınırı işaret ve ne öyle değil.

Godel'in teoremleri (iki tane vardır) temel olarak hem eksiksiz hem de tutarlı olduğu kanıtlanmış bir matematik sistemini tanımlamanın imkansız olduğunu söylüyor. Kurallarınızı tam olarak yapabilmeniz için yapabilir (ele aldığı durum "doğal sayılar için" normal "kurallardı) ya da tutarlılığını kanıtlamanızı mümkün kılabilir, ancak ikisine de sahip olamazsınız.

C gibi bir şey söz konusu olduğunda, doğrudan uygulanmaz - çoğunlukla, sistemin bütünlüğünün veya tutarlılığının “kanıtlanabilirliği” çoğu dil tasarımcısı için yüksek bir öncelik değildir. Aynı zamanda, evet, muhtemelen (en azından bir dereceye kadar) “kusursuz” bir sistem tanımlamanın kesinlikle mümkün olmadığını bilerek etkilendiler - en azından bir dereceye kadar tamamlanmış ve tutarlı. Böyle bir şeyin imkansız olduğunu bilmek, geri adım atmayı, biraz nefes almayı ve tanımlamaya çalışacaklarının sınırlarına karar vermeyi biraz daha kolaylaştırabilirdi.

Kibirle suçlanma (yine bir kez) riski altında, C standardını (kısmen) iki temel fikir tarafından yönetildiğini belirtiyordum:

  1. Dil mümkün olduğu kadar çok çeşitli donanımları desteklemelidir (ideal olarak, tüm donanımları makul bir alt sınıra kadar tüm "akıllıca").
  2. Dil, verilen ortam için mümkün olduğunca çok çeşitli yazılımı yazmayı desteklemelidir.

Birincisi, eğer biri yeni bir işlemci tanımlarsa, bunun için, tasarım en azından makul bir şekilde birkaç basit kılavuza yakın bir yere düştüğü müddetçe, bunun için iyi, sağlam, kullanışlı bir C uygulaması sağlamak mümkün olmalıdır. Von Neumann modelinin genel düzeni hakkında bir şeyler izler ve en azından bir C uygulamasına izin verecek kadar makul miktarda minimum bellek sağlar. "Barındırılan" bir uygulama için (bir işletim sistemi üzerinde çalışan), dosyalara oldukça yakın olan bir kavramı desteklemeniz ve belirli bir minimum karakter grubuna sahip bir karakter kümesine sahip olmanız gerekir (91 gereklidir).

İkinci doğrudan donanımıyla çalışır kod yazmak mümkün olmalıdır demektir, bu nedenle sonuçta vardır sen boot yükleyici, işletim sistemleri, vb herhangi bir işletim sistemi olmadan çalışan gömülü yazılım gibi şeyler yazabilirsiniz bazı bu konuda sınırlarıdır, hemen hemen her pratik işletim sistemi, önyükleyici vb., derleme dilinde yazılmış en az bir miktar kod içermesi muhtemeldir . Aynı şekilde, küçük bir gömülü sistemin bile, ana bilgisayar sistemindeki cihazlara erişim sağlamak için en azından bir tür önceden yazılmış kütüphane rutini içermesi muhtemeldir. Kesin bir sınırın tanımlanması zor olsa da, amaç, bu koda bağımlılığın asgari düzeyde tutulması gerektiğidir.

Dildeki tanımsız davranış, büyük ölçüde dilin bu yetenekleri destekleme niyeti tarafından yönlendirilir. Örneğin, dil isteğe bağlı bir tamsayıyı işaretçiye dönüştürmenize ve bu adreste olanlara erişmenize olanak tanır. Standart yaptığınız zaman ne olacağını söylemeye kalkışmaz (örneğin, bazı adreslerden okumak bile dışarıdan görülebilir etkiler yapabilir). Aynı zamanda, bu tür şeyleri yapmanıza engel olmak için hiçbir girişimde bulunmaz, çünkü C'ye yazmanız gereken bazı yazılım türlerine ihtiyaç duyarsınız .

Diğer tasarım öğeleri tarafından yönlendirilen tanımsız bir davranış var. Örneğin, C'nin bir diğer amacı ayrı derlemeyi desteklemektir. Bu, (örneğin), çoğumuzun bir linkerin normal modeli olarak gördüklerimizi kabaca izleyen bir linker kullanarak parçaları birbirine "bağlamanız" anlamına gelir. Özellikle, ayrı ayrı derlenmiş modülleri, dilin anlambilim bilgisi olmadan eksiksiz bir programda birleştirmek mümkün olmalıdır.

Derleyici teknolojisindeki sınırlamalar nedeniyle ortaya çıkan başka bir tanımlanmamış davranış türü (C ++ 'ta C ++' dan çok daha yaygın) var - temelde bildiğimiz şeyler hatalar ve muhtemelen derleyicinin hatalar olarak teşhis etmesini istiyor, ancak derleyici teknolojisindeki mevcut sınırlar göz önüne alındığında, her koşulda teşhis edilebilecekleri şüphelidir. Bunların birçoğu, ayrı derleme gibi diğer gereksinimlerden kaynaklanmaktadır, bu nedenle, büyük ölçüde çelişkili gereklilikleri dengeleme meselesidir; bu durumda, komite, bazı muhtemel problemleri teşhis etmemek anlamına gelse bile, daha büyük yetenekleri desteklemeyi seçmiştir. olası tüm sorunların teşhis edilmesini sağlamak için yetenekleri sınırlamak yerine.

Bu farklılıklar niyet C ve Java gibi bir şey ya da Microsoft'un CLI tabanlı sistemler arasındaki farkların en sürücü. Sonuncusu açıkça, çok daha sınırlı bir donanım kümesiyle çalışmak veya hedefledikleri daha spesifik donanımı taklit etmek için yazılım talep etmekle sınırlıdır. Ayrıca, özellikle donanımın doğrudan manipülasyonunu önleme niyetindedir , bunun yerine JNI veya P / Invoke (ve C gibi bir yazılı kod) gibi bir girişimde bulunmanızı gerektirir.

Bir an için Godel'in teoremlerine geri dönersek, paralel bir şey çizebiliriz: Java ve CLI "dahili tutarlı" alternatifi seçti, C ise "komple" alternatifi seçti. Tabii ki, bu çok zorlu bir benzetmedir - herhangi birinin her iki durumda da iç tutarlılık ya da bütünlüğünün resmi bir kanıtını denemekten şüpheleniyorum . Bununla birlikte, genel kavram aldıkları seçimlerle oldukça uyumludur.


25
Bence Godel'in Teoremleri kırmızı bir ringa balığıdır. Bir sistemi kendi aksiyomlarından kanıtlayarak uğraşırlar, burada durum böyle değildir: C'nin C'de belirtilmesi gerekmez. Tamamen belirtilen bir dilin olması oldukça olasıdır (bir Turing makinesi düşünün).
poolie

9
Üzgünüm, ama Tanrı'nın Teoremlerini tamamen yanlış anladığınızdan korkuyorum. Bütün gerçek ifadeleri tutarlı bir mantık sistemi içinde ispatlamanın imkansızlığıyla ilgilenirler; hesaplama açısından, eksiklik teoremi, herhangi bir program tarafından çözülemeyen problemlerin olduğunu söylemekle aynıdır - problemler gerçek ifadelere, prova programlarına ve hesaplama modeline benzerdir. Tanımlanmamış davranışlarla hiçbir bağlantısı yoktur. Analojinin bir açıklaması için buraya bakınız: scottaaronson.com/blog/?p=710 .
Alex ten Brink

5
Bir C uygulaması için bir Von Neumann makinesinin gerekli olmadığına dikkat etmeliyim. Harvard mimarisi için bir C uygulaması geliştirmek (ve gömülü sistemler üzerinde bu tür uygulamaların çoğunu görmekten şaşırmam) mükemmel bir şekilde mümkün (ve hatta çok zor değil)
bdonlan

1
Ne yazık ki, modern C derleyici felsefesi UB'yi tamamen yeni bir seviyeye taşıyor. Bir programın belirli bir Tanımlanmamış Davranış biçiminin neredeyse tüm olası "doğal" sonuçlarıyla başa çıkmak için hazırlanmış olduğu durumlarda bile ve ele alınamayanlar en azından tanınabilir niteliktedir (ör. Tamsayı taşması), yeni felsefenin yararı olur UB oluşmadıkça çalıştırılamayan herhangi bir kodu atlayarak, çoğu uygulamada doğru şekilde davranacak olan kodu "daha verimli" olan ancak yalnızca yanlış olan koda dönüştürmek.
supercat,

20

C mantığı açıklıyor

Belirsiz davranış, tanımsız davranış ve uygulama tarafından tanımlanan davranış terimleri, Standart'ın özelliklerini tam olarak tanımlayamayan veya tam olarak tanımlayamayan yazma programlarının sonucunu sınıflandırmak için kullanılır. Bu kategoriyi benimsemenin amacı, uygulama kalitesinin piyasada aktif bir güç olmasına izin veren uygulamalar arasında belirli bir çeşitliliğe izin vermenin yanı sıra, bazı popüler uzantıların Standartlara uygunluk önbelleğini kaldırmadan izin vermektir . Standartlara Ek F, bu üç kategoriden birine giren davranışları kataloglar.

Belirtilmemiş davranış, uygulayıcıya çeviri programlarında bazı enlemler verir. Bu enlem, programı çevirmeyi başaramadıkça uzamaz.

Tanımsız davranış, uygulayıcı lisansına, tanılması zor olan belirli program hatalarını yakalamamasını sağlar. Ayrıca, olası uygun dil uzatımı alanlarını da tanımlar: uygulayıcı, resmi olarak tanımlanmamış davranış tanımını sunarak dili genişletebilir.

Uygulama tanımlı davranış, bir uygulayıcıya uygun yaklaşımı seçme özgürlüğünü verir, ancak bu seçimin kullanıcıya açıklanmasını gerektirir. Uygulama-tanım olarak tanımlanan davranışlar genellikle bir kullanıcının uygulama tanımına dayalı anlamlı kodlama kararları alabileceği davranışlardır. Bir uygulama tanımının ne kadar kapsamlı olması gerektiğine karar verirken uygulayıcılar bu kriteri göz önünde bulundurmalıdır. Belirsiz davranışlarda olduğu gibi, sadece uygulama-tanımlanmış davranışı içeren kaynağın çevrilmemesi yeterli bir cevap değildir.

Önemli, aynı zamanda, programların yararıdır, yalnızca uygulamaların yararıdır. Tanımlanmamış davranışa bağlı bir program, uygun bir uygulama tarafından kabul edilirse , yine de uyumlu olabilir . Tanımsız davranışın varlığı, bir programın uyumsuz hale gelmeden açıkça belirtildiği gibi ("tanımsız davranış") işaretli taşınabilir olmayan özellikleri kullanmasını sağlar. Gerekçe notları:

C kodu taşınabilir olmayabilir. Programcılara gerçek anlamda portatif programlar yazma fırsatı sunma çabasına rağmen, Komite programcıları taşınabilir bir şekilde yazmaya zorlamak istemedi, C'nin “üst düzey bir montajcı” olarak kullanılmasını engelledi: makineye özel yazma yeteneği kod, C'nin güçlü yönlerinden biridir. Büyük ölçüde uyumlu program ve uygun program arasındaki ayrımı çizmeyi büyük ölçüde motive eden bu ilkedir (§1.7).

Ve 1.7 de notları

Üç katlı uyum tanımı, uyumluluk programlarının popülasyonunu genişletmek ve tek bir uygulama ve taşınabilir uygunluk programları kullanarak uygunluk programları arasında ayrım yapmak için kullanılır.

Kesinlikle uyumlu bir program, azami derecede taşınabilir bir program için başka bir terimdir. Amaç, programcıya, portatif olmayan mükemmel bir şekilde yararlı C programları göstermeden, aynı zamanda oldukça taşınabilir olan güçlü C programları yapma mücadelesi vermek. Böylece kesinlikle zarf.

Böylece, GCC'de gayet iyi çalışan bu küçük kirli program hala uygun !


15

Hız meselesi, özellikle C'ye kıyasla bir problemdir. Eğer C ++, ilkel türlerin büyük dizilerini başlatmak gibi, mantıklı olabilecek bazı şeyler yaparsa, C koduna göre bir ton ölçütünü kaybeder. C ++ kendi veri tiplerini başlatır, ancak C tiplerini olduğu gibi bırakır.

Diğer tanımsız davranışlar sadece gerçeği yansıtır. Bir örnek, türden daha büyük bir sayıyla bit kaydırmasıdır. Bu aslında aynı ailenin donanım nesiller arasında farklılık gösterir. 16 bit uygulamanız varsa, aynı ikili dosya 80286 ve 80386 modellerinde farklı sonuçlar verecektir. Yani dil standardı bilmediğimizi söylüyor!

Bazı şeyler belirtildiği gibi alt ifadelerin değerlendirilme sırası gibi tutulur. Aslında, bunun derleyici yazarlarının daha iyi optimizasyonlarına yardımcı olduğuna inanılıyordu. Günümüzde, derleyiciler yine de anlamaya yetecek kadar iyidir, ancak mevcut derleyicilerde özgürlükten yararlanan tüm yerleri bulmanın maliyeti çok yüksektir.


İkinci paragraf için +1, uygulama tarafından tanımlanmış davranış olarak tanımlanması zor olan bir şey gösterir.
David Thornley

3
Bit kaydırma, tanımsız derleyici davranışını kabul etmeye ve donanım özelliklerini kullanmaya yalnızca bir örnek. Sayım türünden daha büyük, ancak bazı donanımlarda uygulanması pahalı olduğunda, bir bit kayması için bir C sonucu belirlemek önemsizdir.
mattnz

7

Bir örnek olarak, işaretçi erişimlerinin neredeyse tanımsız olması ve sadece performans nedenleriyle olması gerekmez. Örneğin, bazı sistemlerde, belirli kayıtları bir işaretçiyle yüklemek, bir donanım istisnası oluşturur. SPARC'da yanlış hizalanmış bir bellek nesnesine erişmek bir veri yolu hatasına neden olur, ancak x86'da "sadece" yavaş olacaktır. Temel donanım ne olacağını belirttiğinden ve bu durumda pek çok donanıma taşınabilir olduğundan C ++ bu davranışı fiilen belirtmek zordur.

Tabii ki, aynı zamanda derleyiciye mimariye özel bilgileri kullanma özgürlüğü verir. Belirtilmemiş bir davranış örneği için, işaretli değerlerin sağa kayması, hangi vardiya işleminin mevcut olmasına izin vermek ve bunun yazılım emülasyonunu zorlamamak için altta yatan donanıma bağlı olarak mantıklı ya da aritmetik olabilir.

Ayrıca derleyici-yazarın işini daha kolay hale getirdiğine inanıyorum, ancak şu an örneği hatırlayamıyorum. Durumu hatırlıyorsam ekleyeceğim.


3
C dili, her zaman byte byte okumaları hizalama kısıtlamaları olan sistemlerde kullanmak zorunda kalacak ve geçersiz adres erişimleri için iyi tanımlanmış davranışlarla istisna tuzakları sağlamak zorunda kalacak şekilde belirtilmiş olabilirdi. Fakat elbette bu, hepsi inanılmaz derecede pahalıya mal olacak (kod büyüklüğü, karmaşıklık ve performans açısından) ve aklı başında kodları düzeltmek için hiçbir fayda sağlamayacaktı.
R.,

6

Basit: Hız ve taşınabilirlik. C ++, geçersiz bir işaretçiyi referanstan çıkarırken bir istisna olduğunu garanti ederse, o zaman yerleşik donanıma taşınabilir olmazdı. Eğer C ++ her zaman ilklendirilen ilkeller gibi bazı şeyleri garanti altına alsaydı, o zaman daha yavaş olurdu ve C ++ 'nın başlangıcında daha yavaş, gerçekten, gerçekten kötü bir şeydi.


1
Ha? İstisnaların gömülü donanım ile ne ilgisi var?
Mason Wheeler,

2
Özel durumlar, sistemi hızlı yanıt vermesi gereken Gömülü Sistemler için çok kötü şekillerde kilitleyebilir. Sahte bir okunmanın yavaşlatılmış bir sisteme çok daha az zarar verdiği durumlar vardır.
Dünya Mühendisi,

1
@Mason: Donanım geçersiz erişimi yakalamak zorunda olduğundan. Windows'un erişim ihlali atması kolaydır ve gömülü donanım için işletim sistemi olmayan, kalıp dışında hiçbir şey yapması daha zordur.
DeadMG

3
Ayrıca, her CPU'nun başlarda donanımdaki geçersiz erişimlere karşı koruma sağlayacak bir MMU'su olmadığını unutmayın. Dilinizden tüm işaretçi erişimlerini kontrol etmesini istemeye başlarsanız, CPU'larda bir MMU'yu bir olmadan taklit etmeniz gerekir - ve böylece HER bellek erişimi çok pahalı hale gelir.
kabarık

4

C 9 bitlik baytlı ve kayan noktalı ünite olmayan bir makinede icat edildi - baytların 9 bitlik, 18 bitlik sözcükler olduğu ve yüzerlerin IEEE754 öncesi aritmatik kullanılarak uygulanması gerektiğini varsayalım?


5
Unix - C'yi düşündüğünüzden şüpheleniyorum, aslında oldukça geleneksel güncel standartlar olan PDP-11'de kullanılmış. Bence temel fikir yine de geçerli.
Jerry Coffin

@Jerry - evet, haklısın - yaşlanıyorum!
Martin Beckett

Yup - Hepimizin başına gelir, korkarım.
Jerry Coffin

4

UB'nin ilk gerekçesinin, derleyiciye optimize etmek için yer bırakması olduğunu düşünmüyorum, ancak sadece mimarilerin şimdiden daha fazla çeşitliliğe sahip olduğu bir zamanda hedefler için bariz uygulamayı kullanma olasılığı (C'nin bir Biraz tanıdık bir mimariye sahip olan PDP-11, ilk liman çok az aşina olan Honeywell 635'tir - kelime adreslenebilir, 36 bit kelime, 6 ya da 9 bit bayt, 18 bit adres kullanarak ... en azından 2 Tamamlayıcı). Ancak, yoğun optimizasyon bir hedef değilse, bariz uygulama, taşma için çalışma zamanı kontrolleri, kayıt büyüklüğü üzerindeki vardiya sayımını içermez; bu, birden fazla değeri değiştiren ifadelerde takma addır.

Dikkate alınan bir diğer şey uygulama kolaylığıydı. O zamanki AC derleyici çoklu işlem kullanarak çoklu geçiştirdi, çünkü bir işlem işlemek her şey mümkün olmazdı (program çok büyük olurdu). Yüksek tutarlılık kontrolü istemek artık yol dışıydı - özellikle birkaç CU içerdiği zaman. (Bunun için C derleyicisinden başka bir program kullanıldı).


UB'nin değişen felsefesini "Programcıların platformları tarafından maruz bırakılan davranışları kullanmasına izin ver" den "Derleyicilerin tamamen tuhaf davranışı uygulamalarına izin vermek için mazeret bul" a ne sürükledim? Ayrıca, kodun yeni derleyici altında çalışacak şekilde değiştirilmesinden sonra bu tür optimizasyonların kod boyutunu nasıl geliştirdiğini merak ediyorum. Pek çok durumda, derleyiciye bu tür "optimizasyonlar" eklemenin tek etkisi, programlayıcıları derleyicinin kırmasını önlemek için daha büyük ve yavaş kod yazmaya zorlamaksa şaşırmam.
Supercat,

POV'da bir sapma. İnsanlar programlarının çalıştığı makinenin daha az farkına varırlardı, taşınabilirlikle daha fazla ilgilendiler; Kıyaslamada en iyi sonuçları elde etmek için optimize ediciler üzerinde baskı vardı ve bu, dillerin özelliklerinden kalan her esneklikten yararlanmak anlamına geliyor. Aynı zamanda, İnternet - Usenet'in bir zamanlar SE'nin bugünlerde - dil avukatlarının, temelde yatan gerekçeyi ve derleyici yazarların davranışlarını önyargılı olarak görme eğiliminde oldukları da bir gerçektir.
AProgrammer

1
Merak ettiğim şey, "C programcıların tanımsız davranışlarda bulunmayacağını varsayıyor" - tarihsel olarak doğru olmayan bir gerçek. Doğru bir ifadeye göre, "Programcıların , bu davranışın doğal platform sonuçlarını ele almaya hazır olmadıkça, standart tarafından tanımlanmayan davranışı tetiklemeyeceği varsayılmıştır . C'nin, sistemin programlama dili olarak tasarlanması göz önüne alındığında, amacının büyük bir kısmı programcıların dil standardı tarafından tanımlanmayan sisteme özgü şeyler yapmalarına izin vermek, asla yapamayacakları fikri saçmadır
supercat

Programcıların, farklı platformların doğal olarak farklı şeyler yapacağı durumlarda taşınabilirliği sağlamak için ekstra çaba sarf etmeleri iyidir , ancak derleyici yazarları, programcıların tarihsel olarak gelecekteki tüm derleyiciler için makul bir şekilde beklenebilecek davranışları ortadan kaldırdıklarında herkesin zamanını boşa harcarlar. Verilen tamsayılar ive nböyle taşan n < INT_BITSve taşmayanlar ; birçok platformda olduğundan daha hızlı ve daha küçük olurdu . Derleyicileri yasaklayan ne kazanılır? i*(1<<n)i<<=n;i=(unsigned)i << n;i*=(1<<N);
supercat,

Standartların UB olarak adlandırdığı birçok şey için tuzaklara izin vermenin iyi olacağını düşünürken (örneğin tamsayı taşması) ve tuzakların tahmin edilebilir bir şey yapmasını istememesinin iyi nedenleri olduğunu düşünüyorum. Çoğu UB formunun belirsiz bir değer vermesi veya başka bir şeyin ne olabileceğini kesin olarak belgelendirmek zorunda kalmadan başka bir şey yapma hakkını saklı tuttukları gerçeğini belgelemesi gerektiği takdirde standart geliştirilecektir. Her şeyi "UB" yapan derleyiciler yasal olacaktır, ancak büyük olasılıkla beğenilmez ...
supercat

3

İlk klasik vakalardan biri tamsayı ekleme olarak imzalandı. Kullanılan işlemcilerin bazılarında, bu bir hataya neden olur ve diğerlerinde ise sadece bir değerle devam eder (muhtemelen uygun modüler değer). Her iki durumun belirtilmesi, tatsız aritmetik stile sahip makineler için programların, tamsayı ekleme gibi bir şey için, koşullu bir dal dahil olmak üzere ekstra kodlara sahip olması gerektiği anlamına gelir.


Tamsayı ekleme, ilginç bir durumdur; Bazı durumlarda yararlı olacak, ancak bazı durumlarda rastgele kod yürütülmesine neden olabilecek tuzak davranışı olasılığının ötesinde, bir derleyicinin tamsayı taşması için belirtilmemiş olduğu gerçeğine dayanarak çıkarımlarda bulunmasının makul olacağı durumlar vardır. Örneğin int, 16 bit ve işaret uzatmalı vardiyaların pahalı olduğu bir derleyici , işaret uzatılmamış bir vardiya kullanılarak hesaplanabilir (uchar1*uchar2) >> 4. Ne yazık ki, bazı derleyiciler sadece sonuçlara değil, aynı zamanda işlenenlere de çıkarımlar getirir.
Supercat,

2

Felsefe hakkında gerçeklikten daha az olduğunu söyleyebilirim - C her zaman bir çapraz platform dili olmuştur ve standart bunu yansıtmak zorundadır ve herhangi bir standardın yayınlandığı tarihte, bir birçok farklı donanım üzerine çok sayıda uygulama. Gerekli davranışı yasaklayan bir standart ya dikkate alınmayacak ya da rekabet eden standartlar birliği oluşturacaktır.


Başlangıçta, pek çok davranış, farklı sistemlerin farklı şeyler yapma olasılığına izin vermek için tanımsız olarak bırakıldı; bunlar, yapılandırılabilir olan veya yapılandırılamayan (ve yapılandırılmadıysa keyfi bir şekilde öngörülemeyen davranışlara neden olabilir) bir işleyiciyle bir donanım tuzağını tetiklemek de dahil. Örneğin, tuzak olmayan bir negatif değerin sola kaymasının istenmesi, böyle bir davranışa dayandığı ve dayandığı bir sistem için tasarlanmış herhangi bir kodu kırar. Kısacası, uygulayıcıların yararlı olduğunu düşündüğü davranışları sağlamalarını engellememek için tanımsız bırakıldılar .
Supercat,

Ancak maalesef, belirli bir durumda yararlı bir şey yapacak bir işlemcide çalıştığını bilen kod bile bu davranıştan yararlanamayacağı için çevrildi, çünkü derleyiciler C standardının kullanmadığı gerçeğini kullanabilir tuhaf dünyayı yeniden yazma kurallarını uygulamak için (platform olsa da) davranışını belirtmeyin.
Supercat,

1

Bazı davranışlar herhangi bir makul yolla tanımlanamaz. Silinen bir işaretçiye erişmek demek istiyorum. Bunu tespit etmenin tek yolu, silme işleminden sonra işaretçi değerini yasaklamaktır (herhangi bir yerde değerini ezberlemek ve herhangi bir ayırma işlevinin artık geri vermesine izin vermemek). Sadece böyle bir ezberleme fazla değildir, fakat uzun süre çalışan bir program için izin verilen işaretçilerin değerlerinin tükenmesine neden olur.


ya da tüm işaretleyicileri ayrı olarak tahsis edebilir weak_ptrve tüm referansları geçersiz kılan bir göstergeye geçersiz kılabilir delete... oh bekle, çöp toplamaya yaklaşıyoruz: /
Matthieu M.

boost::weak_ptrUygulaması, bu kullanım modeli için başlamak için oldukça iyi bir şablondur. weak_ptrsDışarıdan takip etmek ve geçersiz weak_ptrkılmak yerine , bir sadece shared_ptrzayıf sayıma katkıda bulunur ve zayıf sayım temel olarak işaretçinin kendisine bir sayımdır. Böylece shared_ptrhemen silmek zorunda kalmadan iptal edebilirsiniz . Mükemmel değil ( weak_ptrtemelini shared_countiyi bir sebep olmadan sürdürmek için hala çok fazla zaman aşımına uğramış olabilirsiniz ) ama en azından hızlı ve verimli.
kabarık

0

Tanımlanmamış davranıştan başka hiçbir mantıklı seçeneğin olmadığı bir örnek vereceğim. Prensip olarak, herhangi bir işaretçi herhangi bir değişkeni içeren hafızayı işaret edebilir, derleyicinin adresinin asla alınmadığını bildiği küçük yerel değişkenler hariç. Bununla birlikte, modern bir CPU'da kabul edilebilir bir performans elde etmek için, derleyici değişken değerleri kayıtlara kopyalamalıdır. Tamamen bellek dışında çalışmak, başlatıcı değildir.

Bu temelde size iki seçenek sunar:

1) İşaretçinin belirli bir değişkenin belleğine işaret etmesi durumunda, bir göstericiden erişmeden önce her şeyi yazmaçlardan temizleyin. Ardından, değerlerin imleç aracılığıyla değiştirilmesi durumunda, gereken her şeyi tekrar kayıt defterine yükleyin.

2) Bir işaretçinin bir değişkeni diğer ismini kullanmasına izin verilen ve derleyicinin bir işaretçinin bir değişkeni takmadığını varsaymasına izin verildiği zaman için bir kurallar dizisi verin.

Seçenek 2'yi seçer, çünkü 1 performans için korkunç olacaktır. Ancak, bir işaretçi bir değişkeni C kurallarının yasakladığı şekilde değiştirirse ne olur? Etki, derleyicinin gerçekte değişkeni bir kayıt defterinde saklayıp saklamadığına bağlı olduğundan, C standardının belirli sonuçları kesin olarak garanti etmesinin bir yolu yoktur.


"Derleyicinin X doğru gibi davranmasına izin verilir" deyince ile "X'in doğru olmadığı herhangi bir program Tanımsız Davranışa girecek" ifadesiyle anlamsal bir fark olacaktır, ancak ne yazık ki ayrımı açıklığa kavuşturmamak için standartlar vardır. Takma adınız da dahil olmak üzere birçok durumda, önceki ifade, aksi takdirde imkansız olacak birçok derleyici optimizasyonuna izin verir; Sonuncusu biraz daha "optimizasyona" izin verir, fakat son optimizasyonların çoğu programcıların istemeyeceği şeylerdir.
supercat,

Örneğin, bazı kodlar foo42'ye ayarlarsa ve ardından foo44'e ayarlamak için yasadışı olarak değiştirilmiş bir işaretçi kullanan bir yöntem çağırırsa, bir sonraki "meşru" yazının yasal fooolarak okumaya çalıştığını söylemeye yarar görüyorum. 42 verim ya da 44 ve benzeri bir ifade foo+foobile 86 verim olabilir, ama bir lisans içine kimin makul "doğal" davranışlar tüm iyi huylu olurdu Tanımsız Davranışı değiştirme, derleyici genişletilmiş ve hatta geriye dönük çıkarımlar yapmak için izin çok daha az fayda görmek saçma kod oluşturmak için.
supercat,

0

Tarihsel olarak Tanımsız Davranışın iki temel amacı vardı:

  1. Derleyici yazarlarının, asla oluşmaması gereken koşulları ele almak için kod oluşturmasını zorunlu kılmaktan kaçınmak.

  2. Kodun bulunmadığı durumlarda bu koşulların açıkça ele alınması ihtimalinin sağlanması için, uygulamaların bazı durumlarda faydalı olabilecek çeşitli "doğal" davranışları olabilir.

Basit bir örnek olarak, bazı donanım platformlarında, toplamı işaretli bir tamsayıya sığmayacak kadar büyük olan iki pozitif işaretli tamsayıyı bir araya getirmeye çalışmak, belirli bir negatif işaretli tamsayı sağlayacaktır. Diğer uygulamalarda işlemci kapanı tetiklenir. C standardının her iki davranışı da zorunlu kılması için, doğal davranışı standarttan farklı olan platformlar için derleyicilerin, doğru davranışı sağlamak için ekstra kod üretmesi gerekecek - kodun fiili eklenmesi yapmaktan daha pahalı olabilecek kod. Daha da kötüsü, "doğal" davranışı isteyen programcıların bunu başarmak için daha fazla ekstra kod eklemek zorunda kalacağı anlamına gelir (ve fazladan kodun yine ilaveden daha pahalı olacağı anlamına gelir).

Ne yazık ki, bazı derleyici yazarları, derleyicilerin Tanımsız Davranış'ı uyandıracak koşulları bulmak için kendi yollarından gitmesi gerektiği ve bu gibi durumların asla gerçekleşemeyeceği varsayımıyla, bundan daha fazla çıkarımda bulunacakları felsefesini kullandılar. Böylece, 32-bit bir sistemde int, aşağıdaki gibi bir kod verilir:

uint32_t foo(uint16_t q, int *p)
{
  if (q > 46340)
    *p++;
  return q*q;
}

C standardı, derleyicinin q 46341 veya daha büyük olması durumunda, q * q ifadesinin, tanımlanamayan bir intDavranış'a neden olacak şekilde sığmayacak kadar büyük bir sonuç vereceğini ve sonuç olarak derleyicinin, olamaz ve bu nedenle *peğer arttırmak için gerekli olmazdı . Çağıran kod *p, hesaplamanın sonuçlarını atması gerektiğinin bir göstergesi olarak kullanıyorsa , optimizasyonun etkisi, tamsayı taşmasıyla neredeyse hayal edilebilir herhangi bir şekilde performans gösteren sistemler üzerinde mantıklı sonuçlar veren bir kod almak olabilir (yakalama olabilir; çirkin, ama en azından mantıklı olur) ve saçma sapan davranabilecek koda dönüştü.


-6

Verimlilik olağan bahanedir, ancak bahane ne olursa olsun, tanımsız davranış taşınabilirlik için korkunç bir fikirdir. Nitekim tanımsız davranışlar doğrulanmamış, açıklanmamış varsayımlar haline gelir.


7
OP bunu belirledi: “Sorumum tanımlanmamış davranışların ne olduğu ya da gerçekten kötü olduğu hakkında değil. Standarttan ilgili tanımsız davranış tekliflerinin tehlikelerini ve çoğunu biliyorum, bu yüzden ne kadar kötü olduğu konusunda cevaplar göndermekten kaçının. ." Soruyu okumamış gibisin.
Etienne de Martel
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.