Pertanyaan Perilaku tidak terdefinisi dan poin urutan


Apa itu "urutan poin"?

Apa hubungan antara perilaku tidak terdefinisi dan titik urut?

Saya sering menggunakan ekspresi lucu dan berbelit-belit seperti a[++i] = i;, untuk membuat diriku merasa lebih baik. Mengapa saya harus berhenti menggunakannya?

Jika Anda telah membaca ini, pastikan untuk mengunjungi pertanyaan tindak lanjut Perilaku tidak terdefinisi dan poin urutan dimuat ulang.

(Catatan: Ini dimaksudkan sebagai entri untuk FAQ C ++ Stack Overflow. Jika Anda ingin mengkritik gagasan memberikan FAQ dalam formulir ini, maka posting di meta yang memulai semua ini akan menjadi tempat untuk melakukan itu. Jawaban atas pertanyaan itu dipantau dalam C ++ chatroom, di mana ide FAQ dimulai di tempat pertama, jadi jawaban Anda sangat mungkin dibaca oleh mereka yang menemukan ide tersebut.)


897


asal


Jawaban:


C ++ 98 dan C ++ 03

Jawaban ini untuk versi standar C ++ yang lebih lama. Versi C ++ 11 dan C ++ 14 standar tidak secara formal mengandung 'poin urutan'; operasi 'sequenced before' atau 'unsequenced' atau 'tak tentu sequencing' sebagai gantinya. Efek bersihnya pada dasarnya sama, tetapi terminologinya berbeda.


Penolakan : Baik. Jawaban ini agak panjang. Jadi, bersabarlah saat membacanya. Jika Anda sudah tahu hal-hal ini, membacanya lagi tidak akan membuat Anda gila.

Prasyarat : Pengetahuan dasar tentang C ++ Standard 


Apa itu Poin Sequence?

The Standard mengatakan

Pada titik tertentu yang ditentukan dalam urutan eksekusi yang disebut poin urutansemuanya efek samping evaluasi sebelumnya   harus lengkap dan tidak ada efek samping evaluasi berikutnya akan terjadi. (§1.9 / 7)

Efek samping? Apa efek sampingnya?

Evaluasi suatu ekspresi menghasilkan sesuatu dan jika selain itu ada perubahan dalam keadaan lingkungan eksekusi dikatakan bahwa ekspresi (evaluasinya) memiliki beberapa efek samping (s).

Sebagai contoh:

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

Selain operasi inisialisasi nilai y akan berubah karena efek samping ++ operator.

Sejauh ini bagus. Pindah ke poin urutan. Sebuah definisi bergantian dari seq-point yang diberikan oleh penulis comp.lang.c Steve Summit:

Urutan titik adalah titik waktu di mana debu telah menetap dan semua efek samping yang telah dilihat sejauh ini dijamin akan lengkap.


Apa poin urutan umum yang tercantum dalam C ++ Standard?

Yaitu:

  • pada akhir evaluasi ekspresi penuh (§1.9/16) (Ekspresi penuh adalah ekspresi yang bukan merupakan sub ekspresi dari ekspresi lain.)1

Contoh:

int a = 5; // ; is a sequence point here
  • dalam evaluasi masing-masing ekspresi berikut setelah evaluasi ekspresi pertama (§1.9/18) 2

    • a && b (§5.14) 
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18) (di sini, b adalah operator koma; dalam func(a,a++)  , bukan operator koma, itu hanya pemisah antara argumen adan a++. Dengan demikian perilaku tidak terdefinisi dalam kasus itu (jika a dianggap sebagai tipe primitif))
  • pada pemanggilan fungsi (apakah fungsi inline atau tidak), setelah evaluasi semua argumen fungsi (jika ada) yang mana terjadi sebelum pelaksanaan ekspresi atau pernyataan apa pun dalam badan fungsi (§1.9/17).

1: Catatan: evaluasi ekspresi penuh dapat mencakup evaluasi subekspresi yang tidak secara leksikal bagian dari ekspresi penuh. Misalnya, subekspresi yang terlibat dalam mengevaluasi ekspresi argumen default (8.3.6) dianggap dibuat dalam ekspresi yang memanggil fungsi, bukan ekspresi yang mendefinisikan argumen default

2: Operator yang diindikasikan adalah operator built-in, seperti yang dijelaskan dalam klausul 5. Ketika salah satu operator ini kelebihan muatan (klausul 13) dalam konteks yang valid, sehingga menunjuk fungsi operator yang ditentukan pengguna, ekspresi menunjuk pemanggilan fungsi dan operand membentuk daftar argumen, tanpa titik sekuens tersirat di antara mereka.


Apa Perilaku Tidak Terdefinisi?

Standar mendefinisikan Perilaku Tidak Terdefinisi dalam Bagian §1.3.12 sebagai

perilaku, seperti mungkin timbul pada penggunaan program yang salah membangun atau data yang salah, yang mana Standar Internasional ini memaksakan tidak ada persyaratan 3.

Perilaku tidak terdefinisi juga dapat diharapkan saat ini   Standar Internasional menghilangkan deskripsi dari setiap definisi perilaku eksplisit.

 3: perilaku terdefinisi yang diperbolehkan berkisar dari mengabaikan situasi sepenuhnya dengan hasil yang tidak dapat diprediksi, untuk berperilaku selama penerjemahan atau pelaksanaan program secara terdokumentasi dengan karakteristik lingkungan (dengan atau dengan- keluar penerbitan pesan diagnostik), untuk mengakhiri terjemahan atau eksekusi (dengan penerbitan pesan diagnostik).

Singkatnya, berarti perilaku tidak terdefinisi apa pun dapat terjadi dari daemon yang terbang keluar dari hidung Anda ke pacar Anda hamil.


Apa hubungan antara Perilaku Tidak Terdefinisi dan Poin Urutan?

Sebelum saya masuk ke Anda harus mengetahui perbedaan antara Perilaku Tidak Terdefinisi, Perilaku Tidak Ditentukan dan Perilaku Ditetapkan Implementasi.

Anda juga harus tahu itu 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.

Sebagai contoh:

int x = 5, y = 6;

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

Contoh lain sini.


Sekarang Standar di §5/4 kata

  • 1) Antara urutan sebelumnya dan selanjutnya menunjuk objek skalar harus memiliki nilai yang tersimpan dimodifikasi paling banyak sekali oleh evaluasi suatu ekspresi. 

Apa artinya?

Secara informal artinya bahwa antara dua titik berurutan, variabel tidak boleh dimodifikasi lebih dari satu kali. Dalam pernyataan ekspresi, next sequence point biasanya pada titik koma yang diakhiri, dan previous sequence point ada di akhir pernyataan sebelumnya. Suatu ekspresi juga mengandung perantara sequence points.

Dari kalimat di atas, ekspresi berikut ini mengaktifkan Perilaku Tidak Terdefinisi:

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)

Tetapi ekspresi berikut ini baik-baik saja:

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) Selanjutnya, nilai sebelumnya hanya dapat diakses untuk menentukan nilai yang akan disimpan.

Apa artinya? Ini berarti jika suatu objek ditulis ke dalam ekspresi penuh, setiap dan semua mengaksesnya dalam ekspresi yang sama harus terlibat langsung dalam perhitungan nilai yang akan ditulis.

Misalnya dalam i = i + 1 semua akses i (dalam L.H.S dan dalam R.H.S) adalah terlibat langsung dalam perhitungan dari nilai yang akan ditulis. Jadi baik-baik saja.

Aturan ini secara efektif membatasi ekspresi hukum bagi mereka yang aksesnya mendahului modifikasi.

Contoh 1:

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

Contoh 2:

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

dianulir karena salah satu akses dari i (yang di a[i]) tidak ada hubungannya dengan nilai yang akhirnya disimpan di i (yang terjadi di dalam i++), sehingga tidak ada cara yang baik untuk mendefinisikan - baik untuk pemahaman kita atau kompiler - apakah akses harus dilakukan sebelum atau sesudah nilai yang bertambah disimpan. Jadi perilaku itu tidak terdefinisi.

Contoh 3:

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

Tindak lanjut jawaban sini. 


623



Ini adalah tindak lanjut saya jawaban sebelumnya dan berisi C ++ 11 materi terkait..


Prasyarat : Pengetahuan dasar Hubungan (Matematika).


Apakah benar bahwa tidak ada Poin Urutan di C ++ 11?

Iya nih! Ini sangat benar.

Poin Urutan telah digantikan oleh Diurutkan Sebelumnyadan Diurutkan Setelah (dan Tidak Diikutidan Sekuen tak tentu) hubungan di C ++ 11.


Apa sebenarnya hal 'Urutan sebelumnya' ini?

Diurutkan Sebelumnya(§1.9 / 13) adalah relasi yang:

antara evaluasi yang dilakukan oleh satu benang dan menginduksi a urutan parsial yang ketat1

Secara formal itu berarti memberikan dua evaluasi(Lihat di bawah)  Adan B, jika A aku s diurutkan sebelumnya  B, lalu eksekusi A  akan mendahului eksekusi B. Jika A tidak diurutkan sebelumnya Bdan B tidak diurutkan sebelumnya A, kemudian Adan B adalah tidak berurutan  2.

Evaluasi Adan B adalah diurutkan secara tidak tentu kapan juga A diurutkan sebelumnya B atau B diurutkan sebelumnya A, tetapi tidak ditentukan yang mana3.

[NOTES]
  1: Urutan sebagian yang ketat adalah a relasi biner  "<" lebih dari satu set P yang mana asymmetric, dan transitive, i.e., untuk semua a, b, dan c di P, kami punya itu:
 
  ........(saya). jika a <b then¬ (b <a) (asymmetry);
  ........ (ii). jika <b dan b <c kemudian <c (transitivity).
  2: Eksekusi evaluasi yang tidak selesai bisa tumpang tindih.
  3: Evaluasi yang tidak ditentukan secara berurutan tidak bisa tumpang tindih, tetapi bisa dieksekusi lebih dulu.


 Apa arti dari kata 'evaluasi' dalam konteks C ++ 11?

Dalam C ++ 11, evaluasi ekspresi (atau sub-ekspresi) secara umum termasuk:

  • perhitungan nilai (termasuk menentukan identitas suatu objek untuk evaluasi nilai dan mengambil nilai yang sebelumnya ditetapkan ke objek untuk evaluasi prvalue) dan

  • inisiasi efek samping.

Sekarang (§1.9 / 14) mengatakan:

Setiap perhitungan nilai dan efek samping yang terkait dengan ekspresi penuh adalah diurutkan sebelumnya setiap perhitungan nilai dan efek samping yang terkait dengan ekspresi penuh berikutnya untuk dievaluasi.

  • Contoh sepele:

    int x; x = 10; ++x;

    Perhitungan nilai dan efek samping yang terkait dengan ++x disekuensing setelah perhitungan nilai dan efek samping x = 10; 


Jadi pasti ada hubungan antara Perilaku Tidak Terdefinisi dan hal-hal yang disebutkan di atas, bukan?

Iya nih! Kanan.

Di (§1.9 / 15) telah disebutkan itu

Kecuali jika dicatat, evaluasi operan dari masing-masing operator dan sub ekspresi ekspresi individual tidak berurutan4.

Sebagai contoh :

int main()
{
     int num = 19 ;
     num = (num << 3) + (num >> 3);
} 
  1. Evaluasi operan dari + Operator tidak saling berkaitan satu sama lain.
  2. Evaluasi operan dari <<dan >> operator tidak saling berkaitan satu sama lain.

 4: Dalam ekspresi yang dievaluasi lebih dari satu kali selama eksekusi suatu program, tidak berurutandan diurutkan secara tidak tentu evaluasi subekspresi tidak perlu dilakukan secara konsisten dalam evaluasi yang berbeda.

(§1.9 / 15)   Perhitungan nilai dari operand dari suatu   Operator diurutkan sebelum perhitungan nilai dari hasil operator.

Itu artinya dalam x + y perhitungan nilai dari xdan y disekuensing sebelum perhitungan nilai (x + y).

Lebih penting

(§1.9 / 15) Jika efek samping pada objek skalar tidak berurutan relatif baik

(Sebuah) efek samping lain pada objek skalar yang sama 

atau

(b) perhitungan nilai menggunakan nilai objek skalar yang sama.

perilaku itu tidak terdefinisi.

Contoh:

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)

Ketika memanggil fungsi (apakah fungsi inline atau tidak), setiap perhitungan nilai dan efek samping yang terkait dengan ekspresi argumen apa pun, atau dengan ekspresi postfix yang menunjuk fungsi yang dipanggil, diurutkan sebelum pelaksanaan setiap ekspresi atau pernyataan dalam tubuh disebut fungsi. [catatan:  Perhitungan nilai dan efek samping yang terkait dengan ekspresi argumen yang berbeda tidak terjadi. - catatan akhir]

Ekspresi (5), (7)dan (8) jangan gunakan perilaku tidak terdefinisi. Lihat jawaban berikut untuk penjelasan yang lebih rinci.


Catatan Akhir :

Jika Anda menemukan cacat dalam posting, silakan tinggalkan komentar. Power-users (Dengan rep> 20000) harap jangan ragu untuk mengedit posting untuk memperbaiki kesalahan ketik dan kesalahan lainnya.


257



C ++ 17 (N4659) termasuk proposal Memurnikan Urutan Evaluasi Ekspresi untuk Idiomatic C ++ yang mendefinisikan urutan evaluasi ekspresi yang lebih ketat.

Secara khusus, kalimat berikut telah ditambahkan:

8.18 Penugasan dan operator penugasan gabungan:
....

Dalam semua kasus, penugasan diurutkan setelah nilainya   perhitungan operand kanan dan kiri, dan sebelum perhitungan nilai dari ekspresi penugasan.    Operan kanan diurutkan sebelum operan kiri.

Itu membuat beberapa kasus perilaku yang sebelumnya tidak terdefinisi valid, termasuk yang dipertanyakan:

a[++i] = i;

Namun beberapa kasus serupa lainnya masih mengarah pada perilaku tidak terdefinisi.

Di N4140:

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

Tetapi dalam N4659

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

Tentu saja, menggunakan compiler compliant C ++ 17 tidak selalu berarti bahwa seseorang harus mulai menulis ekspresi seperti itu.


13



Saya menduga ada alasan mendasar untuk perubahan, tidak hanya kosmetik untuk membuat interpretasi lama lebih jelas: alasan itu adalah konkurensi. Urutan elaborasi yang tidak ditentukan hanyalah pemilihan salah satu dari beberapa urutan seri yang mungkin, ini sangat berbeda dengan sebelum dan sesudah pemesanan, karena jika tidak ada pemesanan yang ditentukan, evaluasi bersamaan mungkin: tidak demikian dengan aturan lama. Misalnya dalam:

f (a,b)

sebelumnya baik a lalu b, atau, b lalu a. Sekarang, a dan b dapat dievaluasi dengan instruksi interleaved atau bahkan pada core yang berbeda.


11