Tanımsız davranış ve sıralama noktaları


987

"Dizi noktaları" nedir?

Tanımlanmamış davranış ile sıralama noktaları arasındaki ilişki nedir?

a[++i] = i;Kendimi daha iyi hissetmek için sık sık komik ve kıvrımlı ifadeler kullanırım . Onları neden kullanmayı bırakmalıyım?

Bunu okuduysanız, yeniden yüklenen Tanımsız davranış ve sıra noktaları takip sorusunu ziyaret ettiğinizden emin olun .

(Not: Bu, Stack Overflow'ın C ++ SSS'si için bir giriş anlamına gelir . Bu formda bir SSS sağlama fikrini eleştirmek istiyorsanız, tüm bunları başlatan metadaki yayınlama bunu yapmak için yer olacaktır. bu soru SSS fikrinin ilk başta başladığı C ++ sohbet odasında izlenir , bu nedenle cevabınızın bu fikri ortaya çıkaranlar tarafından okunması muhtemeldir.)

Yanıtlar:


683

C ++ 98 ve C ++ 03

Bu yanıt C ++ standardının eski sürümleri içindir. Standardın C ++ 11 ve C ++ 14 sürümleri resmi olarak 'dizi noktaları' içermez; operasyonlar 'önce sıralanır' veya 'sıralanmamış' veya 'belirsiz şekilde sıralanır'. Net etki esasen aynıdır, ancak terminoloji farklıdır.


Feragatname : Tamam. Bu cevap biraz uzun. Okurken sabırlı olun. Bunları zaten biliyorsanız, tekrar okumak sizi delirtmeyecektir.

Önkoşul : Temel C ++ Standardı bilgisi


Dizi Noktaları nedir?

Standart diyor

Yürütme sekansında sekans noktaları adı verilen belirli belirtilen noktalarda , önceki değerlendirmelerin tüm yan etkileri tamamlanmış olacak ve sonraki değerlendirmelerin hiçbir yan etkisi gerçekleşmeyecektir. (/ 7 §1.9)

Yan etkiler? Yan etkileri nelerdir?

Bir ifadenin değerlendirilmesi bir şey üretir ve ek olarak yürütme ortamının durumunda bir değişiklik varsa, ifadenin (değerlendirmesinin) bazı yan etkileri olduğu söylenir.

Örneğin:

int x = y++; //where y is also an int

Başlatma işlemine ek olarak, operatörün yyan etkisi nedeniyle değeri değişir ++.

Çok uzak çok iyi. Sıralama noktalarına geçilir. Comp.lang.c yazarı tarafından verilen seq-noktalarının alternatif bir tanımı Steve Summit:

Sekans noktası, tozun çöktüğü ve şimdiye kadar görülen tüm yan etkilerin tamamlandığı garanti edilen bir noktadır.


C ++ Standardında listelenen ortak sıra noktaları nelerdir?

Onlar:

  • tam ifadenin değerlendirilmesinin sonunda ( §1.9/16) (Tam ifade, başka bir ifadenin alt ifadesi olmayan bir ifadedir.) 1

    Misal :

    int a = 5; // ; is a sequence point here
  • ilk ifadenin değerlendirilmesinden sonra aşağıdaki ifadelerin her birinin değerlendirilmesinde ( §1.9/18) 2

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18)(burada a, b virgül operatörüdür; virgül operatörü func(a,a++) ,değildir, yalnızca argümanlar ave arasında bir ayırıcıdır a++. Dolayısıyla, bu durumda davranış tanımsızdır (eğer ailkel bir tür olarak kabul edilirse )
  • Bir işlev çağrısında (işlev satır içi olsun veya olmasın), işlev gövdesinde ( §1.9/17) ifadeler veya ifadelerin yürütülmesinden önce gerçekleşen tüm işlev bağımsız değişkenlerinin (varsa) değerlendirilmesinden sonra .

1: Not: bir tam ifadenin değerlendirilmesi, tam ifadenin sözlüksel olarak bir parçası olmayan alt ifadelerin değerlendirilmesini içerebilir. Örneğin, varsayılan bağımsız değişken ifadelerinin (8.3.6) değerlendirilmesinde yer alan alt ifadelerin, varsayılan bağımsız değişkeni tanımlayan ifadede değil, işlevi çağıran ifadede oluşturulduğu kabul edilir

2: Belirtilen işleçler, 5. maddede açıklandığı gibi yerleşik işleçlerdir. Bu işleçlerden biri geçerli bir bağlamda aşırı yüklendiğinde (madde 13), böylece kullanıcı tanımlı bir işleç işlevi belirlendiğinde, ifade bir işlev çağırma ve işlenenler, aralarında zımni bir sıra noktası bulunmayan bir argüman listesi oluşturur.


Tanımsız Davranış Nedir?

Standart Bölüm Tanımsız davranışı tanımlar §1.3.12olarak

davranış, bu standard yüklediği kendisi için hatalı bir program yapısının veya hatalı veriler, kullanımı üzerine ortaya çıkabilecek gibi hiçbir gereksinimleri 3 .

Bu Uluslararası Standart, herhangi bir açık davranış tanımının açıklamasını atladığında tanımlanmamış davranışlar da beklenebilir.

3: izin verilen tanımlanamayan davranış, durumu tamamen öngörülemeyen sonuçlarla görmezden gelmekten, çeviri veya program yürütme sırasında ortamın karakteristik bir şekilde (bir tanılama iletisinin verilmesiyle veya verilmesiyle), bir çeviri veya yürütmenin sona erdirilmesine kadar değişir. (bir teşhis mesajı verilmesi ile).

Kısacası, tanımsız davranış , burnunuzdan uçan cinlerden kız arkadaşınıza hamile kalmaya kadar her şeyin olabileceği anlamına gelir .


Tanımsız Davranış ve Sıra Noktaları arasındaki ilişki nedir?

Buna girmeden önce Tanımsız Davranış, Belirsiz Davranış ve Uygulama Tanımlı Davranış arasındaki farkları bilmelisiniz .

Bunu da bilmelisin the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

Örneğin:

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

Burada başka bir örnek .


Şimdi Standart §5/4diyor ki

  • 1) Önceki ve sonraki dizi noktası arasında, bir skaler nesne, bir ifadenin değerlendirilmesi ile depolanmış değerinin en fazla bir kez değiştirilmiş olması gerekir.

Bunun anlamı ne?

Gayri resmi olarak, iki dizi noktası arasında bir değişkenin birden fazla değiştirilmemesi gerektiği anlamına gelir. Bir ifade ifadesinde, next sequence pointgenellikle sonlandırma noktalı virgülünde ve previous sequence pointönceki ifadenin sonundadır. Bir ifade ara madde de içerebilir sequence points.

Yukarıdaki cümleden aşağıdaki ifadeler Tanımsız Davranışı çağırır:

i++ * ++i;   // UB, i is modified more than once btw two SPs
i = ++i;     // UB, same as above
++i = 2;     // UB, same as above
i = ++i + 1; // UB, same as above
++++++i;     // UB, parsed as (++(++(++i)))

i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)

Ancak aşağıdaki ifadeler iyidir:

i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i);   // well defined 
int j = i;
j = (++i, i++, j*i); // well defined

  • 2) Ayrıca, önceki değere sadece depolanacak değerin belirlenmesi için erişilebilir.

Bunun anlamı ne? Bu, bir nesnenin tam bir ifade içinde yazılması durumunda, aynı ifade içindeki herhangi bir erişimin ve tüm erişimlerin, yazılacak değerin hesaplanmasında doğrudan yer alması gerektiği anlamına gelir .

Örneğin (LHS ve RHS'de) i = i + 1tüm erişim, yazılacak değerin hesaplanmasında doğrudan yer alır . Yani iyi.i

Bu kural, yasal ifadeleri, erişimin modifikasyondan açıkça önce geçtiği ifadelerle etkili bir şekilde kısıtlar.

Örnek 1:

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

Örnek 2:

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

izin verilmez, çünkü i(içinde olanın a[i]) erişimlerinden birinin i'de depolanan değerle ilgisi yoktur (içinde gerçekleşir i++) ve bu yüzden tanımlamak için iyi bir yol yoktur - ya bizim anlayışımız için ya da derleyicinin - erişimin, artan değerin kaydedilmesinden önce veya sonra gerçekleşip gerçekleşmeyeceği. Yani davranış tanımsız.

Örnek 3:

int x = i + i++ ;// Similar to above

Burada C ++ 11 için cevap izleyin .


45
*p++ = 4 Tanımsız Davranış değildir. *p++olarak yorumlanır *(p++). p++döndürür p(bir kopya) ve önceki adreste saklanan değeri döndürür . Neden UB'yi çağırsın ki? Mükemmel para cezası.
Prasoon Saurav

6
@Mike: AFAIK, bağlayabileceğiniz C ++ Standardının (yasal) bir kopyası yok.
sbi

11
Peki, ISO'nun ilgili sipariş sayfasına bir bağlantınız olabilir. Her neyse, bunu düşünerek, "C ++ Standardının temel bilgisi" ifadesi, terimler açısından biraz çelişkili görünüyor, çünkü eğer standardı okuyorsanız, temel seviyeyi geçersiniz. Belki dilde ifade sözdizimi, işlem sırası ve belki de operatör aşırı yüklenmesi gibi temel anlayışa ihtiyacınız olan şeyleri listeleyebiliriz
Mike DeSimone

41
Standart alıntı yeni başlayanlar öğretmek için en iyi yol olduğundan emin değilim
Ters

6
@Adrian İlk ifade bir UB'yi çağırır, çünkü son ++iile atama arasında bir sıralama noktası yoktur i. İfade ideğerini değiştirmediğinden, ikinci ifade UB'yi çağırmaz i. İkinci örnekte , atama işleci çağrılmadan önce i++bir sıralama noktası ( ,) gelir.
Kolyunya

276

Bu benim önceki cevabımın takibi ve C ++ 11 ile ilgili materyal içeriyor. .


Önkoşul : Temel ilişkiler bilgisi (Matematik).


C ++ 11'de Dizi Noktaları olmadığı doğru mu?

Evet! Bu çok doğrudur.

Sıra Noktaları , C ++ 11'de Sıralı Sıralı ve Sıralı (ve Sıralanmamış ve Belirsiz Sıralı ) ilişkilerle değiştirildi .


Bu 'Önceden sekanslanmış' şey tam olarak nedir?

Önceden Sıralanmış (§1.9 / 13) bir ilişkidir:

Değerlendirmeler arasında tek bir iş parçacığı tarafından yürütülen ve katı kısmi bir düzen indükler 1

Resmi olarak iki değerlendirme yapılması (aşağıya bakınız) anlamına gelir Ave Beğer Adaha önce dizilmişse B , yürütme A işleminden önce yapılacaktır B. Eğer Adaha önce sıralandı edilmez Bve Bdaha önce sıralandı edilmez A, daha sonra Ave Bvardır unsequenced 2 .

Değerlendirmeler Ave Bdaha önce dizilenmiş veya daha önce dizilenmiş olduğunda belirsiz bir şekilde dizilenmiştir , ancak hangi 3 belirtilmemiş .ABBA

[NOTLAR]
1: Katı bir kısmi sıralama a, ikili bir ilişki "<" kümesi üzerinde Polduğu asymmetricve transitivetüm, diğer bir deyişle a, bve ciçinde P: o sahip
........, (i). eğer bir <b sonra ¬ (b <a) ( asymmetry);
........, (ii). eğer <b ve b <c ise o zaman <c ( transitivity).
2: yürütülmesi dizilenmemiş değerlendirmeler için üst üste .
3: belirsiz dizilenmiştir değerlendirmeler olamaz örtüşme , ama her iki ilk önce yerine getirilmesine olabilir.


C ++ 11 bağlamında 'değerlendirme' kelimesinin anlamı nedir?

C ++ 11'de genel olarak bir ifadenin (veya bir alt ifadenin) değerlendirilmesi şunları içerir:

Şimdi (§1.9 / 14) diyor ki:

Bir tam ifadeyle ilişkili her değer hesaplaması ve yan etki, değerlendirilecek bir sonraki tam ifadeyle ilişkili her değer hesaplamasından ve yan etkiden önce sıralanır .

  • Önemsiz örnek:

    int x; x = 10; ++x;

    Değer hesaplaması ve ilişkili yan etki ++x, değer hesaplaması ve yan etkisinden sonra sıralanır.x = 10;


Yani Tanımsız Davranış ile yukarıda belirtilen şeyler arasında bir ilişki olmalı, değil mi?

Evet! Sağ.

(§1.9 / 15) 'de

Belirtilenler dışında, münferit operatörlerin işlenenlerinin ve münferit ifadelerin alt ifadelerinin değerlendirmeleri sıralanmamıştır 4 .

Örneğin :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. +Operatörün işlenenlerinin değerlendirilmesi birbirine göre sıralanmamıştır.
  2. İşlenenler değerlendirilmesi <<ve >>operatörlerin birbirlerine unsequenced göredir.

4: Bir programın yürütülmesi sırasında bir kereden fazla değerlendirilen bir ifadede , alt ifadelerinin sıralanmamış ve belirsiz biçimde sıralı değerlendirmelerinin farklı değerlendirmelerde tutarlı bir şekilde yapılması gerekmez.

(§1.9 / 15) Bir operatörün işlenenlerinin değer hesaplamaları, operatörün sonucunun değer hesaplamasından önce sıralanır.

Bu demektir ki x + ydeğeri hesaplama xve ydeğeri hesaplama önce dizilir (x + y).

Daha önemlisi

(§1.9 / 15) Skaler bir cisim üzerindeki bir yan etki ikisinden birine göre sıralanmamışsa

(a) aynı skaler nesne üzerinde başka bir yan etki

veya

(b) aynı skaler nesnenin değerini kullanarak bir değer hesaplaması.

davranış tanımsız .

Örnekler:

int i = 5, v[10] = { };
void  f(int,  int);
  1. i = i++ * ++i; // Undefined Behaviour
  2. i = ++i + i++; // Undefined Behaviour
  3. i = ++i + ++i; // Undefined Behaviour
  4. i = v[i++]; // Undefined Behaviour
  5. i = v[++i]: // Well-defined Behavior
  6. i = i++ + 1; // Undefined Behaviour
  7. i = ++i + 1; // Well-defined Behaviour
  8. ++++i; // Well-defined Behaviour
  9. f(i = -1, i = -1); // Undefined Behaviour (see below)

Bir işlevi çağırırken (işlev satır içi olsun veya olmasın), herhangi bir bağımsız değişken ifadesiyle veya çağrılan işlevi belirten postfix ifadesiyle ilişkili her değer hesaplaması ve yan etki, işlev. [ Not: Farklı argüman ifadeleriyle ilişkili değer hesaplamaları ve yan etkiler sıralanmamıştır . - son not ]

İfadeler (5), (7)ve (8)tanımsız davranışını çağırmak. Daha ayrıntılı bir açıklama için aşağıdaki cevaplara göz atın.


Son Not :

Eğer postada herhangi bir kusur bulursanız lütfen yorum bırakın. Uzman kullanıcılar (rep> 20000 ile) yazım hatalarını ve diğer hataları düzeltmek için lütfen yazıyı düzenlemekte tereddüt etmeyin.


3
"Asimetrik" yerine, önce / sonra sıralanan "antisimetrik" ilişkilerdir. Bu, daha sonra verilecek olan kısmi bir düzenin tanımına (Vikipedi ile de uyumlu) uyması için metinde değiştirilmelidir.
TemplateRex

1
Son örnekteki 7) öğesi neden bir UB? Belki de olmalı f(i = -1, i = 1)?
Mikhail

1
Ben "önce dizildi" ilişkisinin açıklamasını düzeltti. Bu bir olan sıkı kısmi sıralama . Açıkçası, bir ifade kendisinden önce dizilemez, dolayısıyla ilişki dönüşlü olamaz. Bu nedenle asimetriktir, anti-simetrik değildir.
ThomasMcLeod

1
5) İyi davranılmak aklımı uçurdu. Johannes Schaub'un açıklaması tam olarak açık değildi. Özellikle de ++i( +onu kullanan operatörden önce değerlendirilen değer olarak) bile , standart hala yan etkisinin bitmesi gerektiğini söylemiyor. Bir bir ref döndürdüğü için Ama aslında, lvaluehangi ideğerlendirme bitirilmesi gerektiğinden kendisini, bu bitirdikten GEREKİR yan etkisi, bu nedenle değer güncel olması gerekmektedir. Bu aslında çılgınca bir şeydi.
v.oddou

"ISO C ++ Komitesi üyeleri, Sıra Noktaları öğelerinin anlaşılmasının oldukça zor olduğunu düşündüler. Bu nedenle, daha açık bir ifade ve daha fazla hassasiyet için yukarıda belirtilen ilişkilerle değiştirmeye karar verdiler." - bu iddia için bir referansınız var mı? Bana öyle geliyor ki yeni ilişkileri anlamak daha zor.
MM

30

C ++ 17 ( N4659), daha katı bir ifade değerlendirme sırasını tanımlayan Deyimsel C ++ için İfade Değerlendirme Sırasını İnceleyen bir teklif içerir .

Özellikle, aşağıdaki cümle

8.18 Atama ve bileşik atama operatörleri :
....

Her durumda, atama, sağ ve sol işlenenlerin değer hesaplamasından sonra ve atama ifadesinin değer hesaplamasından önce sıralanır. Sağ işlenen, sol işlenenden önce sıralanır.

aşağıdaki açıklama ile birlikte

Bir ekspresyon X, bir ifade önce sekanslanacak söylenen Y'nin her değeri hesaplaması ve ifade ile ilişkili her bir yan etki, eğer X , her bir değer hesaplama ve ifadesi ile ilişkili her bir yan etki önce sekanslanır Y .

söz konusu davranış da dahil olmak üzere önceden tanımlanmamış birkaç davranışı geçerli kılmak:

a[++i] = i;

Bununla birlikte, diğer bazı benzer durumlar hala tanımlanmamış davranışa yol açmaktadır.

İçinde N4140:

i = i++ + 1; // the behavior is undefined

Fakat N4659

i = i++ + 1; // the value of i is incremented
i = i++ + i; // the behavior is undefined

Tabii ki, C ++ 17 uyumlu bir derleyici kullanmak, mutlaka bu tür ifadeler yazmaya başlamak anlamına gelmez.


neden i = i++ + 1;c ++ 17'de davranış tanımlanır, "Sağ işlenen sol işlenenden önce sıralı" olsa bile, ancak "i ++" için değişiklik ve atama için yan etki sıralanmamış olsa da, lütfen bunları yorumlamak için daha fazla ayrıntı verin
jack X

@jackX Cevabı uzattım :).
AlexD

Evet, sanırım cümle yorumlama detayı "Sağ işlenen, sol işlenmeden önce sıralanmıştır" daha yararlıdır. sol işlenenden önce sıralanır. yaptığınız gibi :-)
jack X

11

Değişikliğin temel bir nedeni olduğunu tahmin ediyorum, eski yorumu daha açık hale getirmek sadece kozmetik değil: bu sebep eşzamanlılıktır. Belirlenmemiş detaylandırma sırası, sadece birkaç olası seri siparişten birinin seçilmesidir, bu siparişlerden önce ve sonra oldukça farklıdır, çünkü belirlenmiş bir sipariş yoksa, eşzamanlı değerlendirme mümkündür: eski kurallarda böyle değildir. Örneğin:

f (a,b)

önceden ya sonra b, ya da, b sonra a. Artık a ve b, araya eklenmiş talimatlarla veya hatta farklı çekirdeklerde değerlendirilebilir.


5
Yine de, eğer 'a' veya 'b' işlev çağrısı içeriyorsa, bunların sıralanmamış olmak yerine belirsiz bir şekilde sıralandığına inanıyorum, yani birinden gelen tüm yan etkilerin, diğer, derleyicinin hangisinin önce olacağı konusunda tutarlı olmasına gerek yoktur. Bu artık doğru değilse, örtüşmeyen işlemlere dayanan çok sayıda kodu kıracaktır (örneğin, 'a' ve 'b' her biri paylaşılan bir statik durumu kurar, kullanır ve kaldırırsa).
Supercat

2

Gelen C99(ISO/IEC 9899:TC3)hangi bugüne kadar aşağıdaki steteents evaluaiton sırasına ilişkin yapılan bu tartışma yer almıyor gibi.

[...] alt ifadelerin değerlendirme sırası ve yan etkilerin gerçekleşme sırası belirtilmemiş. (Bölüm 6.5 s. 67)

İşlenenlerin değerlendirme sırası belirtilmemiştir. Bir atama operatörünün sonucunu değiştirmek veya bir sonraki sekans noktasından sonra ona erişmek için girişimde bulunulursa, [sic] davranışı tanımlanmamıştır (Bölüm 6.5.16 sf 91).


2
Soru, C değil C ++ olarak etiketlenir, çünkü C ++ 17'deki davranış, eski sürümlerdeki davranıştan oldukça farklıdır ve C11, C99, C90 vb. onunla ilişki. Genel olarak, bunu kaldırmanızı öneririm. Daha da önemlisi, C için eşdeğer Soru ve Cevapları bulmamız ve bunun doğru olduğundan emin olmamız gerekir (ve özellikle C ++ 17'nin kuralları değiştirdiğini not eder - C ++ 11 ve önceki davranışlar aşağı yukarı aynıdır) C11'de, onu C'de tarif eden sözlü ifade hala 'dizi noktaları' kullanırken C ++ 11 ve üstü kullanmıyor.
Jonathan Leffler
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.