Jawaban teratas adalah kesalahpahaman yang salah (tapi umum):
Perilaku tak terdefinisi adalah properti run-time *. Ini TIDAK BISA "perjalanan waktu"!
Operasi tertentu ditentukan (oleh standar) memiliki efek samping dan tidak dapat dioptimalkan. Operasi yang melakukan I / O atau yang mengakses volatile
variabel termasuk dalam kategori ini.
Namun , ada peringatan: UB bisa saja berperilaku, termasuk perilaku yang membatalkan operasi sebelumnya. Ini dapat memiliki konsekuensi yang serupa, dalam beberapa kasus, untuk mengoptimalkan kode sebelumnya.
Faktanya, ini konsisten dengan kutipan di jawaban teratas (penekanan saya):
Implementasi yang sesuai dengan menjalankan program yang dibentuk dengan baik akan menghasilkan perilaku yang dapat diamati yang sama sebagai salah satu dari kemungkinan eksekusi dari mesin abstrak yang sesuai dengan program yang sama dan input yang sama.
Namun, jika eksekusi semacam itu mengandung operasi yang tidak ditentukan, Standar Internasional ini tidak mensyaratkan pelaksanaan yang menjalankan program tersebut dengan masukan tersebut (bahkan tidak berkaitan dengan operasi sebelum operasi tidak ditentukan pertama).
Ya, kutipan ini mengatakan "bahkan tidak berkaitan dengan operasi sebelum operasi pertama yang tidak ditentukan" , tetapi perhatikan bahwa ini secara khusus tentang kode yang sedang dijalankan , tidak hanya dikompilasi.
Bagaimanapun, perilaku tidak terdefinisi yang sebenarnya tidak tercapai tidak melakukan apa-apa, dan agar baris yang berisi UB benar-benar tercapai, kode yang mendahuluinya harus dieksekusi terlebih dahulu!
Jadi ya, sekali UB dijalankan , semua efek dari operasi sebelumnya menjadi tidak terdefinisi. Tapi sampai itu terjadi, eksekusi program sudah terdefinisi dengan baik.
Namun, perhatikan bahwa semua eksekusi program yang mengakibatkan terjadinya hal ini dapat dioptimalkan untuk program yang setara , termasuk program apa pun yang menjalankan operasi sebelumnya tetapi kemudian membatalkan efeknya. Akibatnya, kode sebelumnya dapat dioptimalkan kapan pun melakukannya akan setara dengan efeknya yang dibatalkan ; jika tidak, tidak bisa. Lihat contoh di bawah.
* Catatan: Ini tidak bertentangan dengan UB yang terjadi pada waktu kompilasi . Jika kompiler memang dapat membuktikan bahwa kode UB akan selalu dieksekusi untuk semua input, maka UB dapat memperpanjang waktu kompilasi. Namun, ini membutuhkan pengetahuan bahwa semua kode sebelumnya pada akhirnya kembali , yang merupakan persyaratan yang kuat. Sekali lagi, lihat di bawah untuk contoh / penjelasan.
Untuk membuat ini konkret, perhatikan bahwa kode berikut harus mencetak foo
dan menunggu masukan Anda terlepas dari perilaku tidak terdefinisi yang mengikutinya:
printf("foo")
getchar()
*(char*)1 = 1
Namun, perhatikan juga bahwa tidak ada jaminan yang foo
akan tetap ada di layar setelah UB terjadi, atau karakter yang Anda ketikkan tidak lagi berada di buffer input; kedua operasi ini dapat "diurungkan", yang memiliki efek serupa dengan "perjalanan waktu" UB.
Jika getchar()
garis itu tidak ada, itu akan menjadi hukum bagi garis yang akan dioptimalkan pergi jika dan hanya jika yang akan dibedakan dari keluaran foo
dan kemudian "un-melakukan" itu.
Apakah keduanya tidak bisa dibedakan atau tidak akan bergantung sepenuhnya pada implementasinya (yaitu pada compiler dan library standar Anda). Misalnya, dapatkah Anda printf
memblokir utas Anda di sini sambil menunggu program lain membaca hasilnya? Atau akankah segera kembali?
Jika dapat memblokir di sini, maka program lain dapat menolak untuk membaca keluaran penuhnya, dan mungkin tidak pernah kembali, dan akibatnya UB mungkin tidak pernah benar-benar terjadi.
Jika dapat segera kembali ke sini, maka kita tahu ia harus kembali, dan oleh karena itu mengoptimalkannya sama sekali tidak dapat dibedakan dari menjalankannya dan kemudian menghentikan pengaruhnya.
Tentu saja, karena compiler mengetahui perilaku apa yang diperbolehkan untuk versi tertentu printf
, ia dapat mengoptimalkannya, dan akibatnya printf
dapat dioptimalkan dalam beberapa kasus dan tidak pada yang lain. Tapi, sekali lagi, pembenarannya adalah bahwa ini tidak bisa dibedakan dengan UB yang tidak melakukan operasi sebelumnya, bukan kode sebelumnya yang "diracuni" karena UB.
a
tidak digunakan (kecuali untuk menghitung sendiri) dan cukup hapusa