Bu kodu "Programlama Dili" 4. C edition bölüm 36.3.6 iyi tanımlanmış bir davranış var mı?
Bjarne StroustrupThe C Programming Language4 baskı bölümü 36.3.6
STL gibi İşlemleraşağıdaki kodu chaining bir örnek olarak kullanılır:
void f2()
{
std::string s = "but I have heard it works even if you don't believe in it" ;
s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
.replace( s.find( " don't" ), 6, "" );
assert( s == "I have heard it works only if you believe in it" ) ;
}
gcc
assert başarısız (see it liveve Visual Studio
(see it live), ama Clang kullanırken başarısız değildirsee it live).
Neden farklı sonuçlar alıyorum? Bu Derleyiciler yanlış zincirleme ifade değerlendirme veya bu kodu unspecified undefined behavior çeşit sergi mu?
CEVAP
Kod sergiler belirtilmemiş davranışı nedeniyle belirlenemeyen sipariş değerlendirilmesi alt ifadeleri rağmen değil çağırmak tanımsız davranış bu yana tüm yan etkileri yapılan işlevlerinde which introduces a sequencing relationship arasındaki yan etkileri bu durumda.
Bu örnek, söz konusu kod ile ilgili önerisi şöyle diyor ki N4228: Refining Expression Evaluation Order for Idiomatic C söz edilir:
[...]Bu kodu C uzman-geniş dünya ve yayınlanan tarafından incelendi (C Programlama Dili, 4thedition.) Henüz, onun güvenlik açığı değerlendirme belirtilmemiş amacıyla yeni keşfedildi bir aracı tarafından[...]
Ayrıntılar
İşlevler için bağımsız değerlendirme belirlenemeyen bir emir olduğunu çok açık olabilir ama bu davranış zincirleme işlev çağrıları ile nasıl etkileşim kurduğu muhtemelen kadar açıktır. Ben bu davayı ilk ve görünüşe göre tüm bu analiz bana belli değildiuzman yorumcularya.
İlk bakışta her yana replace
soldan sağa ilgili işlev bağımsız değişken gruplar gruplar olarak değerlendirilmesi gerektiğine soldan sağa değerlendirilecek kadar iyi gibi görünebilir.
Bu yanlıştır, fonksiyon argümanları var belirlenemeyen bir sipariş değerlendirilmesi, ancak zincirleme işlev çağrıları yapıyor takdim sola doğru değerlendirilmesi amacıyla, her bir işlev çağrısı, bağımsız değişkenlerin her bir işlev çağrısı sadece sıralı öncesi ile ilgili üye işlevini çağırın onlar bir parçası. Özellikle bu aşağıdaki çağrı etkileri:
s.find( "even" )
ve:
s.find( " don't" )
indeterminately ilişkin olarak sıralı olan:
s.replace(0, 4, "" )
iki find
çağrı olabilir değerlendirdi önce veya sonra replace
, hangi konularda'den bu yana bir yan etkisi s
bir şekilde bu değiştirme sonucu find
değişiyor uzunluğu s
. Böylece 24 ** zaman bağlı olarak sonuç değişir find
iki arama göre değerlendirilir.
Zincirleme ifade baktığımız ve alt ifadeler: bazı değerlendirme sırası inceleyin
s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
^ ^ ^ ^ ^ ^ ^ ^ ^
A B | | | C | | |
1 2 3 4 5 6
ve:
.replace( s.find( " don't" ), 6, "" );
^ ^ ^ ^
D | | |
7 8 9
Not 4
7
daha da fazla-deyimler alt bölünebilir olduğu gerçeğini göz ardı ediyoruz. Yani:
A
D
önce sıralı olanC
önce sıralı olanB
önce sıralı- Özel durumlar Aşağıda bazı
9
34 *indeterminately saygı ile sıralı alt ifadeler için diğer3
1
B
önce sıralı6
4
C
önce sıralı9
7
D
önce sıralı
Bu sorunun anahtarı olduğunu:
9
45 * indeterminately sıralıB
saygı ile
B
açısından 4
7
değerlendirme seçim potansiyel sırası f2()
değerlendirirken clang
gcc
sonuçlar arasındaki farkı açıklar. Benim testleri clang
gcc
sonra değerlendirir iken 4
57* değerlendirme önce B
değerlendirir. Aşağıdaki test programı her bir durumda neler olduğunu göstermek için kullanabiliriz:
#include <iostream>
#include <string>
std::string::size_type my_find( std::string s, const char *cs )
{
std::string::size_type pos = s.find( cs ) ;
std::cout << "position " << cs << " found in complete expression: "
<< pos << std::endl ;
return pos ;
}
int main()
{
std::string s = "but I have heard it works even if you don't believe in it" ;
std::string copy_s = s ;
std::cout << "position of even before s.replace(0, 4, \"\" ): "
<< s.find( "even" ) << std::endl ;
std::cout << "position of don't before s.replace(0, 4, \"\" ): "
<< s.find( " don't" ) << std::endl << std::endl;
copy_s.replace(0, 4, "" ) ;
std::cout << "position of even after s.replace(0, 4, \"\" ): "
<< copy_s.find( "even" ) << std::endl ;
std::cout << "position of don't after s.replace(0, 4, \"\" ): "
<< copy_s.find( " don't" ) << std::endl << std::endl;
s.replace(0, 4, "" ).replace( my_find( s, "even" ) , 4, "only" )
.replace( my_find( s, " don't" ), 6, "" );
std::cout << "Result: " << s << std::endl ;
}
gcc
sonuç (see it live)
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position don't found in complete expression: 37
position even found in complete expression: 26
Result: I have heard it works evenonlyyou donieve in it
clang
sonuç (see it live):
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position even found in complete expression: 22
position don't found in complete expression: 33
Result: I have heard it works only if you believe in it
Visual Studio
sonuç (see it live):
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position don't found in complete expression: 37
position even found in complete expression: 26
Result: I have heard it works evenonlyyou donieve in it
Standart ayrıntılar
Alt ifadeler belirtilen değerlendirmeler unsequenced sürece, bu 66 ** draft C 11 standard bölümünden olduğunu biliyoruzProgramın çalışmasıdiyor ki:
Belirtilmediği, operatörlerin işlenen değerlendirmeler dışında ve bireysel ifadelerin taşıyıcının unsequenced var.[...]
ve bir işlev çağrısı işlevi, sıralı önce bir ilişki 1.9
bölüm: fonksiyon gövdesi için saygı ile postfix ifade ve argümanlar aramalar tanıttı
Bir işlevi veya işlev satır içi olup olmadığını () çağrılırken [...], her değer hesaplama ve yan etkisi herhangi bir bağımsız değişken ile ilişkili ifade veya sonek ifade denir belirleme ile işlev, daha önce sıralı her deyim yürütme ya çağrılan işlev gövdesindeki deyim.[...]
Biz de üye erişim sınıfı ve bu nedenle zincirleme bölüm 5.2.5
soldan sağa değerlendirirSınıf üyesi erişimdiyor ki:
[...]Nokta ya da ok değerlendirilir önce postfix ifade;64 bu değerlendirme sonucunda, kimliği ifade ile birlikte, belirler tüm postfix ifadenin sonucu.
Not, bu durumda buradakimliği ifadestatik olmayan bir üye olmak biter değerlendirme sırasını belirtmek değil işleviifade listesio zamandan beri ()
içinde alt-ifade ayrı. 5.2
ilgili gramerPostfix ifadeler:
postfix-expression:
postfix-expression ( expression-listopt) // function call
postfix-expression . templateopt id-expression // Class member access, ends
// up as a postfix-expression
Ne ":-!!&"C kodu?...
"Varsayılan davranış;git push" ol...
Kullanarak " koyuyorsun;sayfa özel...
&Quot;coalgebra" programlama bağl...
Nasıl "yorum-out" bir toplu ...