C ve C ++ 'da tanımsız davranış nedir? Belirtilmemiş davranış ve uygulama tanımlı davranış ne olacak? Onların arasındaki fark ne?
C ve C ++ 'da tanımsız davranış nedir? Belirtilmemiş davranış ve uygulama tanımlı davranış ne olacak? Onların arasındaki fark ne?
Yanıtlar:
Tanımsız davranış , C ve C ++ dilinin diğer dillerden gelen programcılar için şaşırtıcı olabilecek yönlerinden biridir (diğer diller daha iyi gizlemeye çalışır). Temel olarak, birçok C ++ derleyicisi programda herhangi bir hata rapor etmese bile, öngörülebilir bir şekilde davranmayan C ++ programları yazmak mümkündür!
Klasik bir örneğe bakalım:
#include <iostream>
int main()
{
char* p = "hello!\n"; // yes I know, deprecated conversion
p[0] = 'y';
p[5] = 'w';
std::cout << p;
}
Değişken p
dize değişmezine işaret eder "hello!\n"
ve aşağıdaki iki atama bu dize değişmez değerini değiştirmeye çalışır. Bu program ne yapıyor? C ++ standardının 2.14.5. Paragrafına göre, tanımlanmamış davranışı başlatır :
Bir dizgi değişmezini değiştirmeye çalışmanın etkisi tanımlanmamıştır.
İnsanları çığlık duyabiliyorum "Ama bekle, bunu hiçbir sorun derlemeyebilir ve çıktı alabilirim yellow
" veya "Ne demek tanımsız, dizgi değişmezleri salt okunur bellekte saklanır, böylece ilk atama denemesi çekirdek dökümü ile sonuçlanır". Bu tam olarak tanımlanmamış davranış sorunudur. Temel olarak, standart, tanımlanmamış davranışları (hatta burun iblisleri) çağırdıktan sonra her şeyin olmasına izin verir. Dilin zihinsel modelinize göre "doğru" bir davranış varsa, bu model yanlıştır; C ++ standardının tek oylama süresi vardır.
Tanımlanmamış davranış diğer örnekleri olarak, sınırlarının ötesinde bir dizi erişim içerir boş işaretçiyi kaldırma , bunların kullanım süresi sona erdikten sonra da nesneleri erişme ya da yazılı olduğu iddia edilen akıllı ifadeler gibi i++ + ++i
.
C ++ standardının 1.9 bölümü, tanımlanmamış davranışın daha az tehlikeli iki kardeşini, belirtilmemiş davranışı ve uygulama tanımlı davranışı belirtmektedir :
Bu Uluslararası Standarttaki semantik açıklamalar, parametreleştirilmiş belirsiz bir soyut makineyi tanımlar.
Soyut makinenin belirli yönleri ve işlemleri bu Uluslararası Standartta uygulama tanımlı olarak tanımlanmaktadır (örneğin
sizeof(int)
). Bunlar soyut makinenin parametrelerini oluşturur. Her uygulama, bu bakımdan özelliklerini ve davranışını açıklayan belgeleri içermelidir.Soyut makinenin diğer bazı yönleri ve işlemleri bu Uluslararası Standartta belirtilmemiş olarak tanımlanır (örneğin, bir işleve yönelik argümanların değerlendirme sırası). Mümkün olduğunda, bu Uluslararası Standart bir dizi izin verilebilir davranış tanımlar. Bunlar soyut makinenin belirsiz olmayan yönlerini tanımlar.
Bazı diğer işlemler bu Uluslararası Standartta tanımlanmamış olarak tanımlanmıştır (örneğin, boş göstergenin kayıttan çıkarılmasının etkisi). [ Not : bu Uluslararası Standart, tanımlanmamış davranış içeren programların davranışları için herhangi bir gereklilik getirmez. - son not ]
Özellikle, bölüm 1.3.24:
İzin verilen tanımlanamayan davranış durumu öngörülemeyen sonuçlarla tamamen görmezden gelmekten, çeviri veya programın yürütülmesi sırasında ortamın karakteristiği olan (tanı mesajı verilmiş veya verilmeden) belgelenmiş bir şekilde davranmaya, bir çeviriyi veya yürütmeyi sonlandırmaya kadar değişir. bir teşhis mesajı).
Tanımlanmamış davranışa girmekten kaçınmak için ne yapabilirsiniz? Temel olarak, neden bahsettiklerini bilen yazarların iyi C ++ kitaplarını okumalısınız . Vidalı internet öğreticileri. Vida bullschildt.
int f(){int a; return a;}
: a
işlevi, işlev çağrıları arasında değişebilir.
Bu temelde standarttan düz bir kopyala yapıştır
3.4.1 1 Her bir uygulamanın seçimin nasıl yapıldığını belgelediği, uygulama tanımlı davranış belirtilmemiş davranış
2 ÖRNEK Uygulama tanımlı davranışa bir örnek, işaretli bir tamsayı sağa kaydırıldığında yüksek dereceli bitin yayılmasıdır.
3.4.3 1 taşınabilir olmayan veya hatalı program yapısı veya hatalı verilerin kullanılması üzerine tanımlanmamış davranış davranışı, bu Uluslararası Standart hiçbir gereklilik getirmez
2 NOT Olası tanımlanamayan davranışlar durumu öngörülemeyen sonuçlarla tamamen görmezden gelmekten, çeviri veya program yürütme sırasında ortamın karakteristik bir şekilde (bir tanılama mesajı vererek veya vermeden) yürütmeye, bir çeviri veya yürütmeyi sonlandırmaya kadar değişir. bir teşhis mesajı verilmesi).
3 ÖRNEK Tanımlanmamış davranışa örnek olarak tamsayı taşması davranışı gösterilebilir.
3.4.4 1 belirtilmemiş davranış belirtilmemiş bir değerin kullanılması veya bu standardın iki veya daha fazla olasılık sağladığı ve herhangi bir durumda seçilen başka bir şart getirmediği başka bir davranış
2 ÖRNEK Belirtilmemiş davranışa bir örnek, bir işleve yönelik argümanların değerlendirilme sırasıdır.
int foo(int x) { if (x >= 0) launch_missiles(); return x << 1; }
Tanımsız Davranış çağırmak füzeyi yok işlevini çağırmak elbette bu yana, bu çağrıyı yapabilir belirleyebilir bir derleyici launch_missiles()
koşulsuz.
Belki de kolay ifade etme, standartların titiz tanımından daha kolay olabilir.
uygulama tanımlı davranış
Dil, veri türlerine sahip olduğumuzu söylüyor. Derleyici satıcıları hangi boyutları kullanacaklarını belirtir ve yaptıklarının belgelerini sağlar.
tanımlanmamış davranış
Yanlış bir şey yapıyorsunuz. Örneğin, int
uymayan bir değerde çok büyük bir değere sahipsiniz char
. Bu değeri nasıl yerleştiriyorsunuz char
? aslında bir yolu yok! Her şey olabilir, ama en mantıklı olan şey bu int'in ilk baytını alıp içine koymak olacaktır char
. İlk baytı atamak için bunu yapmak yanlıştır, ancak başlık altında olan şey budur.
belirtilmemiş davranış
İlk önce bu ikisinin hangi işlevi yürütülür?
void fun(int n, int m);
int fun1()
{
cout << "fun1";
return 1;
}
int fun2()
{
cout << "fun2";
return 2;
}
...
fun(fun1(), fun2()); // which one is executed first?
Dil, soldan sağa veya sağdan sola değerlendirmeyi belirtmez! Bu nedenle, tanımlanmamış bir davranış tanımlanmamış bir davranışla sonuçlanabilir veya sonuçlanmayabilir, ancak kesinlikle programınız tanımlanmamış bir davranış üretmemelidir.
@ESKay Bence daha fazla netleştirmek için sorunuzun cevabını düzenlemeye değer :)
çünkü
fun(fun1(), fun2());
"uygulama tanımlı" davranışı değil mi? Sonuçta derleyici birini mi yoksa diğerini mi seçmeli?
Uygulama tanımlı ve belirtilmemiş arasındaki fark, derleyicinin ilk durumda bir davranış seçmesi gerektiğidir, ancak ikinci durumda bunu yapmak zorunda değildir. Örneğin, bir uygulamanın yalnızca bir tanımına sahip olması gerekir sizeof(int)
. Yani, sizeof(int)
programın bir kısmı için 4, diğerleri için 8 olduğu söylenemez . Derleyicinin Tamam diyebileceği belirtilmemiş davranışların aksine, bu argümanları soldan sağa değerlendireceğim ve bir sonraki işlevin argümanları sağdan sola değerlendirilecek. Aynı programda olabilir, bu yüzden belirtilmemiş olarak adlandırılır . Aslında, belirtilmemiş bazı davranışlar belirtilirse C ++ daha kolay hale getirilebilirdi. Dr.Stustustrup'un bunun cevabına bir göz atın :
Derleyiciye bu özgürlüğü vererek üretilebilecek ile “sıradan soldan sağa değerlendirme” gerektiren farkın önemli olabileceği iddia edilmektedir. İkna olmadım, ama özgürlüğün "dışarıda" sayısız derleyicisinden yararlanarak ve özgürlüğü tutkuyla savunan bazı insanlar, bir değişikliğin zor olacağını ve C ve C ++ dünyalarının uzak köşelerine nüfuz etmesinin on yıllar sürebileceğini söyledi. Ben tüm derleyiciler ++ i + i ++ gibi koda karşı uyarmak hayal kırıklığına uğrattı. Benzer şekilde, argümanların değerlendirme sırası da belirtilmemiştir.
IMO çok fazla "şey" tanımsız, belirtilmemiş, uygulama tanımlı vb. Olarak bırakılmıştır. Ancak, bu söylemesi kolay ve hatta örnekler vermek, ancak düzeltilmesi zor. Ayrıca, sorunların çoğundan kaçınmanın ve taşınabilir kod üretmenin o kadar da zor olmadığı belirtilmelidir.
fun(fun1(), fun2());
davranış değil "implementation defined"
mi? Sonuçta derleyici birini mi yoksa diğerini mi seçmeli?
"I am gonna evaluate these arguments left-to-right and the next function's arguments are evaluated right-to-left"
bunun olduğunu anlıyorum can
. Gerçekten, bugünlerde kullandığımız derleyiciler ile mi?
Resmi C Gerekçe Belgesinden
Terimleri belirtilmemiş davranışı, tanımsız davranış ve uygulama tanımlı davranış, özellikleri Standart etmeyen, ya tamamen tarif edemez programları yazma sonucunu kategorilere ayırmak için kullanılır. Bu kategorileştirmeyi benimsemenin amacı, uygulama kalitesinin piyasada aktif bir güç olmasına izin veren uygulamalar arasında belirli bir çeşitliliğe izin vermek ve Standarda uygunluk önbelleğini kaldırmadan belirli popüler uzantılara izin vermektir. Standart F'ye Ek F, bu üç kategoriden birine giren davranışları kataloglar.
Belirtilmemiş davranış , çevirmene programları çevirmede bir miktar enlem verir. Bu enlem, programın tercüme edilemediği kadar uzanmaz.
Tanımsız davranış , uygulayıcıya, teşhis edilmesi zor bazı program hatalarını yakalamaması için lisans verir. Ayrıca, uygun dil uzantısının mümkün olduğu alanları da tanımlar: uygulayıcı, resmi olarak tanımlanmamış davranışın bir tanımını sağlayarak dili artırabilir.
Uygulama tanımlı davranış, bir uygulayıcıya uygun yaklaşımı seçme özgürlüğü verir, ancak bu seçimin kullanıcıya açıklanmasını gerektirir. Uygulama tanımlı olarak belirlenen davranışlar genellikle bir kullanıcının uygulama tanımına dayalı olarak 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 dikkate almalıdır. Belirtilmemiş davranışlarda olduğu gibi, yalnızca uygulama tanımlı davranışı içeren kaynağı çevirememek yeterli bir yanıt değildir.
Tanımsız Davranış ve Belirsiz Davranışın kısa bir açıklaması vardır.
Nihai özeti:
Özetle, yazılımınızın taşınabilir olması gerekmedikçe, belirtilmemiş davranışlar genellikle endişelenmemeniz gereken bir şeydir. Tersine, tanımlanmamış davranış her zaman istenmez ve asla gerçekleşmemelidir.
Tarihsel olarak, hem Uygulama Tanımlı Davranış hem de Tanımsız Davranış, Standart yazarlarının, kalite uygulamaları yazan kişilerin, varsa, hangi uygulama alanında çalışan amaçlanan uygulama alanındaki programlar için karar vereceğini karar vermelerini bekledikleri durumları temsil eder. hedeflenen hedefler. Üst düzey sayı-crunching kodunun ihtiyaçları, düşük seviyeli sistem kodundan oldukça farklıdır ve hem UB hem de IDB, derleyici yazarlarına bu farklı ihtiyaçları karşılamak için esneklik sağlar. Her iki kategori de uygulamaların belirli bir amaç için, hatta herhangi bir amaç için yararlı bir şekilde davranmasını zorunlu kılmaz. Bununla birlikte, belirli bir amaca uygun olduğunu iddia eden kalite uygulamaları, bu amaca uygun bir şekilde davranmalıdır.Standardın gerektirip gerektirmediği .
Uygulama Tanımlı Davranış ve Tanımsız Davranış arasındaki tek fark, ilkinin uygulamanın hiçbir şeyin yararlı olamayacağı durumlarda bile tutarlı bir davranış tanımlamasını ve belgelemesini gerektirmesidir . Aralarındaki ayrım çizgisi, uygulamaların davranışları tanımlamasının genellikle yararlı olup olmayacağı değil (derleyici yazarları, Standardın gerektirip gerektirmediği pratik olduğunda yararlı davranışları tanımlamalıdır) değil, bir davranışın tanımlanmasının aynı anda maliyetli olacağı uygulamalar olup olmadığıdır. ve işe yaramaz . Bu tür uygulamaların var olabileceğine dair bir karar, hiçbir şekilde, tanımlanmış bir davranışın diğer platformlarda desteklenmesinin yararlılığına ilişkin herhangi bir yargı anlamına gelmez.
Ne yazık ki, 1990'ların ortalarından beri derleyici yazarları, davranışsal yetmezliklerin yokluğunu, davranışsal garantilerin hayati oldukları uygulama alanlarında ve hatta neredeyse hiçbir maliyeti olmadığı sistemlerde bile maliyete değmeyeceğine dair bir yargı olarak yorumlamaya başladılar. Derleyici yazarlar UB'ye makul bir yargılama daveti olarak davranmak yerine, bunu yapmamak için bir bahane olarak görmeye başladılar .
Örneğin, aşağıdaki kod verildiğinde:
int scaled_velocity(int v, unsigned char pow)
{
if (v > 250)
v = 250;
if (v < -250)
v = -250;
return v << pow;
}
ikinin tamamlayıcı uygulamasının, ifadeyi olumlu ya da olumsuz v << pow
olup olmadığına bakılmaksızın ikinin tamamlayıcı kayması olarak ele almak için herhangi bir çaba sarf etmesi gerekmez v
.
Bununla birlikte, günümüzün derleyici yazarları arasında tercih edilen felsefe, ancak v
programın Tanımsız Davranışla meşgul olması durumunda negatif olabileceğinden, programın negatif aralığını kırpması için bir neden olmadığını öne sürecektir v
. Her bir anlamlı derleyicide desteklenen negatif değerlerin sola kaydırılması ve mevcut kodun büyük bir kısmı bu davranışa dayanmasına rağmen, modern felsefe, Standardın sola kaydırılan negatif değerlerin UB olduğunu söyler. derleyici yazarlarının bunu görmezden gelmesi gerektiğini ima eder.
<<
negatif sayılar üzerinde UB olması kötü bir küçük tuzak ve bunu hatırlatmak için sevindim!
i+j>k
ek taşmanın olduğu durumlarda 1 veya 0 verimi olup olmadığını umursamıyorsa , bir derleyici, programcı kodu olarak yazarsa mümkün olmayacak bazı büyük optimizasyonlar yapabilir . (int)((unsigned)i+j) > k
C ++ standardı n3337 § 1.3.10 uygulama tanımlı davranış
iyi biçimlendirilmiş bir program yapısı ve doğru veriler için, uygulamaya ve her bir uygulama belgesine bağlı olarak
Bazen C ++ Standardı bazı yapılara belirli bir davranış getirmez, bunun yerine belirli, iyi tanımlanmış bir davranışın belirli bir uygulama (kütüphane sürümü) tarafından seçilmesi ve tanımlanması gerektiğini söyler . Böylece, Standard bunu tanımlamasa bile, kullanıcı programın nasıl davranacağını tam olarak bilebilir.
C ++ standardı n3337 § 1.3.24 tanımlanmamış davranış
bu standardın hiçbir gereklilik getirmediği davranış [Not: Bu standardın açık bir davranış tanımını atlaması veya bir program hatalı bir yapı veya hatalı veri kullanması durumunda tanımlanmamış davranış beklenebilir. İzin verilen tanımlanamayan davranış durumu öngörülemeyen sonuçlarla tamamen görmezden gelmekten, çeviri veya programın yürütülmesi sırasında ortamın karakteristiği olan (tanı mesajı verilmiş veya verilmeden) belgelenmiş bir şekilde davranmaya, bir çeviriyi veya yürütmeyi sonlandırmaya kadar değişir. bir teşhis mesajı). Birçok hatalı program yapısı tanımlanmamış davranış oluşturmaz; teşhis edilmeleri gerekir. - son not]
Program C ++ Standardına göre tanımlanmamış bir yapı ile karşılaştığında, yapmak istediği her şeyi yapmasına izin verilir (belki bana bir e-posta gönderebilir veya size bir e-posta gönderebilir veya kodu tamamen göz ardı edebilir).
C ++ standardı n3337 § 1.3.25 belirtilmemiş davranış
iyi biçimlendirilmiş bir program yapısı ve uygulamaya bağlı olarak doğru veriler için davranış [Not: Uygulama, hangi davranışın oluştuğunu belgelemek için gerekli değildir. Olası davranışların kapsamı genellikle bu Uluslararası Standart tarafından tanımlanır. - son not]
C ++ Standardı bazı yapılara belirli bir davranış getirmez, bunun yerine belirli bir uygulama (kütüphane sürümü) tarafından belirli , iyi tanımlanmış bir davranışın seçilmesi gerektiğini ( bot gerekli değildir ) söylüyor . Dolayısıyla, herhangi bir açıklama yapılmadığında, kullanıcının programın nasıl davranacağını tam olarak bilmek zor olabilir.
Uygulama tanımlı-
Uygulayıcılar iyi belgelenmelidir, standart seçenekler sunar ama derlediğinizden emin olun
Belirtilmemiş -
Uygulama tanımlı ile aynı, ancak belgelenmedi
Tanımsız-
Herhangi bir şey olabilir, onunla ilgilen.
uint32_t s;
, 33'ün 1u<<s
ne zaman değerlendirildiğinin s
belki 0 verim ya da verim 2 vermesi beklenebilirdi, ama tuhaf başka bir şey yapmazdı. Bununla birlikte, daha yeni derleyiciler, 1u<<s
bir derleyicinin s
önceden 32'den az olması gerektiği için, bu ifadeden önce veya sonra yalnızca s
32 veya daha büyük olsaydı ilgili olabilecek herhangi bir kodun atlanabileceğini belirlemesine neden olabilir.