SORU
26 Kasım 2014, ÇARŞAMBA


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.6STL 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
26 Kasım 2014, ÇARŞAMBA


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ı olan C önce sıralı olan B önce sıralı
  • Özel durumlar Aşağıda bazı 9 34 *indeterminately saygı ile sıralı alt ifadeler için diğer
    • 3 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

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Andytokkallos

    Andytokkallo

    27 Kasım 2007
  • Kai Moosmann

    Kai Moosmann

    5 Temmuz 2006
  • max2sims2

    max2sims2

    19 Kasım 2008