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 'diurutkan sebelum' atau 'tidak diurutkan' atau 'diurutkan tak tentu'. Efek bersihnya pada dasarnya sama, tetapi terminologinya berbeda.
Penafian : Oke. 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 Urutan?
Standar mengatakan
Pada titik tertentu tertentu dalam urutan eksekusi yang disebut titik sekuens , semua efek samping dari evaluasi sebelumnya harus lengkap dan tidak ada efek samping dari evaluasi selanjutnya yang akan terjadi. (§1.9 / 7)
Efek samping? Apa efek sampingnya?
Evaluasi suatu ekspresi menghasilkan sesuatu dan jika selain itu ada perubahan dalam kondisi lingkungan eksekusi dikatakan bahwa ekspresi (evaluasinya) memiliki beberapa efek samping.
Sebagai contoh:
int x = y++; //where y is also an int
Selain operasi inisialisasi, nilai y
akan berubah karena efek samping dari ++
operator.
Sejauh ini bagus. Pindah ke titik urutan. Definisi pergantian poin seq yang diberikan oleh penulis comp.lang.c Steve Summit
:
Titik sekuens adalah titik di mana debu telah mengendap dan semua efek samping yang telah terlihat sejauh ini dijamin akan lengkap.
Apa titik urutan umum yang tercantum dalam Standar C ++?
Yaitu:
pada akhir evaluasi ekspresi penuh ( §1.9/16
) (Ekspresi penuh adalah ekspresi yang bukan merupakan subekspresi 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 a, b adalah operator koma; di func(a,a++)
,
bukan operator koma, itu hanya pemisah antara argumen a
dan a++
. Dengan demikian perilaku tidak terdefinisi dalam kasus itu (jika a
dianggap sebagai tipe primitif))
pada panggilan fungsi (apakah fungsi inline atau tidak), setelah evaluasi semua argumen fungsi (jika ada) yang terjadi sebelum eksekusi ekspresi atau pernyataan apa pun di badan fungsi ( §1.9/17
).
1: Catatan: evaluasi ekspresi penuh dapat mencakup evaluasi subekspresi yang bukan bagian leksikal 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 ditunjukkan adalah operator built-in, seperti yang dijelaskan dalam klausa 5. Ketika salah satu dari operator ini kelebihan beban (klausa 13) dalam konteks yang valid, dengan demikian menunjuk fungsi operator yang ditentukan pengguna, ekspresi menunjuk pada pemanggilan fungsi dan operan membentuk daftar argumen, tanpa titik urutan tersirat di antara mereka.
Apa itu Perilaku Tidak Terdefinisi?
Standar ini mendefinisikan Perilaku Tidak Terdefinisi dalam Bagian §1.3.12
sebagai
perilaku, seperti yang mungkin timbul pada saat penggunaan konstruksi program yang keliru atau data yang salah, yang untuknya Standar Internasional ini tidak menetapkan persyaratan 3 .
Perilaku tidak terdefinisi juga dapat diharapkan ketika Standar Internasional ini menghilangkan deskripsi dari setiap definisi perilaku yang eksplisit.
3: perilaku yang tidak terdefinisi yang diperbolehkan berkisar dari mengabaikan situasi sepenuhnya dengan hasil yang tidak terduga, hingga berperilaku selama penerjemahan atau pelaksanaan program dengan cara yang terdokumentasi dengan karakteristik lingkungan (dengan atau tanpa penerbitan pesan diagnostik), hingga penghentian terjemahan atau eksekusi (dengan penerbitan pesan diagnostik).
Singkatnya, perilaku yang tidak terdefinisi berarti segala sesuatu dapat terjadi mulai dari daemon yang terbang keluar dari hidung Anda hingga pacar Anda hamil.
Apa hubungan antara Perilaku Tidak Ditentukan dan Poin Urutan?
Sebelum saya membahasnya, Anda harus mengetahui perbedaan antara Perilaku Tidak Terdefinisi, Perilaku Tidak Ditentukan, dan Perilaku yang Didefinisikan 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 di sini .
Sekarang Standar dalam §5/4
kata
- 1) Antara titik urutan sebelumnya dan berikutnya objek skalar harus memiliki nilai tersimpan dimodifikasi paling banyak sekali dengan evaluasi suatu ekspresi.
Apa artinya?
Secara tidak resmi itu berarti bahwa antara dua titik urutan suatu variabel tidak boleh dimodifikasi lebih dari sekali. Dalam pernyataan ekspresi, next sequence point
biasanya di titik koma terminasi, dan previous sequence point
di akhir pernyataan sebelumnya. Ekspresi juga dapat mengandung perantara sequence points
.
Dari kalimat di atas, ekspresi berikut memunculkan 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)
Tapi ekspresi berikut 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 harus diakses hanya untuk menentukan nilai yang akan disimpan.
Apa artinya? Ini berarti jika suatu objek ditulis ke dalam ekspresi penuh, setiap dan semua akses ke sana dalam ekspresi yang sama harus secara langsung terlibat dalam perhitungan nilai yang akan ditulis .
Misalnya dalam i = i + 1
semua akses i
(dalam LHS dan dalam RHS) secara langsung terlibat dalam perhitungan nilai yang akan ditulis. Jadi tidak apa-apa.
Aturan ini secara efektif membatasi ekspresi hukum pada akses yang secara nyata 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
tidak diizinkan karena salah satu akses i
(yang masuk a[i]
) tidak ada hubungannya dengan nilai yang akhirnya disimpan di i (yang terjadi berulang i++
), dan jadi tidak ada cara yang baik untuk mendefinisikan - baik untuk pemahaman kita atau compiler's - apakah akses harus dilakukan sebelum atau setelah nilai yang bertambah disimpan. Jadi perilakunya tidak terdefinisi.
Contoh 3:
int x = i + i++ ;// Similar to above
Tindak lanjut jawaban untuk C ++ 11 di sini .
*p++ = 4
bukan Perilaku Tidak Terdefinisi.*p++
diartikan sebagai*(p++)
.p++
mengembalikanp
(salinan) dan nilai yang tersimpan di alamat sebelumnya. Mengapa itu memohon pada UB? Tidak apa-apa.