Perilaku tidak terdefinisi, tidak ditentukan, dan ditentukan implementasi

530

Apa perilaku tidak terdefinisi dalam C dan C ++? Bagaimana dengan perilaku yang tidak ditentukan dan perilaku yang ditentukan implementasi? Apa perbedaan di antara mereka?

Zolomon
sumber
1
Saya cukup yakin bahwa kami telah melakukan ini sebelumnya, tetapi saya tidak dapat menemukannya. Lihat juga: stackoverflow.com/questions/2301372/…
dmckee --- ex-moderator kitten
1
Berikut ini adalah diskusi yang menarik (bagian "Lampiran L dan Perilaku Tidak Terdefinisi").
Owen

Jawaban:

406

Perilaku tidak terdefinisi adalah salah satu aspek dari bahasa C dan C ++ yang dapat mengejutkan bagi programmer yang berasal dari bahasa lain (bahasa lain mencoba menyembunyikannya dengan lebih baik). Pada dasarnya, adalah mungkin untuk menulis program C ++ yang tidak berperilaku dengan cara yang dapat diprediksi, meskipun banyak kompiler C ++ tidak akan melaporkan kesalahan dalam program!

Mari kita lihat contoh klasik:

#include <iostream>

int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

Variabel pmenunjuk ke string literal "hello!\n", dan dua tugas di bawah ini mencoba untuk memodifikasi string literal itu. Apa yang dilakukan program ini? Menurut bagian 2.14.5 paragraf 11 dari standar C ++, ini memanggil perilaku yang tidak terdefinisi :

Efek dari upaya untuk memodifikasi string literal tidak terdefinisi.

Saya dapat mendengar orang-orang berteriak, "Tapi tunggu, saya bisa mengkompilasi ini tanpa masalah dan mendapatkan output yellow" atau "Apa maksud Anda, string literal disimpan dalam memori read-only, sehingga upaya penugasan pertama menghasilkan dump inti". Ini persis masalah dengan perilaku yang tidak terdefinisi. Pada dasarnya, standar ini memungkinkan apa pun terjadi setelah Anda mengaktifkan perilaku yang tidak terdefinisi (bahkan setan hidung). Jika ada perilaku "benar" menurut model mental bahasa Anda, model itu salah; Standar C ++ memiliki satu-satunya suara, titik.

Contoh lain dari perilaku tidak terdefinisi termasuk mengakses array di luar batasnya, mendereferensi pointer nol , mengakses objek setelah masa hidup mereka berakhir atau menulis ekspresi yang diduga seperti pintari++ + ++i .

Bagian 1.9 dari standar C ++ juga menyebutkan dua saudara lelaki yang berperilaku tidak terdefinisi kurang berbahaya, perilaku yang tidak ditentukan dan perilaku yang didefinisikan implementasi :

Deskripsi semantik dalam Standar Internasional ini mendefinisikan mesin abstrak nondeterministik yang diparameterisasi.

Aspek dan operasi tertentu dari mesin abstrak dijelaskan dalam Standar Internasional ini sebagai yang ditentukan implementasi (misalnya, sizeof(int)). Ini merupakan parameter dari mesin abstrak. Setiap implementasi harus mencakup dokumentasi yang menggambarkan karakteristik dan perilakunya dalam hal ini.

Aspek dan operasi tertentu lainnya dari mesin abstrak dijelaskan dalam Standar Internasional ini sebagai tidak spesifik (misalnya, urutan evaluasi argumen untuk suatu fungsi). Jika memungkinkan, Standar Internasional ini menetapkan seperangkat perilaku yang diperbolehkan. Ini menentukan aspek nondeterministic dari mesin abstrak.

Operasi tertentu lainnya dijelaskan dalam Standar Internasional ini sebagai tidak terdefinisi (misalnya, efek dereferencing penunjuk nol). [ Catatan : Standar Internasional ini tidak menetapkan persyaratan pada perilaku program yang mengandung perilaku tidak terdefinisi. - catatan akhir ]

Secara khusus, bagian 1.3.24 menyatakan:

Perilaku yang tidak terdefinisi yang diizinkan berkisar dari mengabaikan situasi sepenuhnya dengan hasil yang tidak terduga , hingga berperilaku selama penerjemahan atau pelaksanaan program dengan cara yang terdokumentasi sebagai karakteristik lingkungan (dengan atau tanpa penerbitan pesan diagnostik), hingga penghentian terjemahan atau eksekusi (dengan penerbitan pesan diagnostik).

Apa yang dapat Anda lakukan untuk menghindari perilaku tidak terdefinisi? Pada dasarnya, Anda harus membaca buku C ++ yang bagus oleh penulis yang tahu apa yang mereka bicarakan. Sekrup tutorial internet. Sekrup bullschildt.

fredoverflow
sumber
6
Ini adalah fakta aneh yang dihasilkan dari penggabungan bahwa jawaban ini hanya mencakup C ++ tetapi tag pertanyaan ini mencakup C. C memiliki gagasan yang berbeda tentang "perilaku tidak terdefinisi": Masih akan membutuhkan implementasi untuk memberikan pesan diagnostik bahkan jika perilaku juga dinyatakan untuk menjadi tidak terdefinisi untuk pelanggaran aturan tertentu (pelanggaran kendala).
Johannes Schaub - litb
8
@ Benit Ini adalah perilaku yang tidak terdefinisi karena standar mengatakan itu perilaku yang tidak terdefinisi, titik. Pada beberapa sistem, string literal sebenarnya disimpan di segmen teks hanya-baca, dan program akan macet jika Anda mencoba untuk memodifikasi string literal. Pada sistem lain, string literal memang akan tampak berubah. Standar tidak mengamanatkan apa yang harus terjadi. Itulah arti perilaku yang tidak terdefinisi.
fredoverflow
5
@FredOverflow, Mengapa kompiler yang bagus memungkinkan kami untuk mengkompilasi kode yang memberikan perilaku tidak terdefinisi? Apa gunanya kompilasi kode semacam ini? Mengapa tidak semua kompiler yang baik memberi kami tanda peringatan merah besar ketika kami mencoba untuk mengkompilasi kode yang memberikan perilaku tidak terdefinisi?
Pacerier
14
@Pacerier Ada beberapa hal yang tidak dapat diperiksa pada waktu kompilasi. Sebagai contoh tidak selalu mungkin untuk menjamin bahwa pointer nol tidak pernah direferensikan, tetapi ini tidak terdefinisi.
Tim Seguine
4
@Celeritas, perilaku tidak terdefinisi bisa menjadi non-deterministik. Sebagai contoh, tidak mungkin untuk mengetahui sebelumnya apa isi dari memori yang tidak diinisialisasi, misalnya. int f(){int a; return a;}: nilai adapat berubah di antara panggilan fungsi.
Tandai
97

Nah, ini pada dasarnya adalah copy-paste langsung dari standar

3.4.1 1 perilaku yang ditentukan implementasi perilaku yang tidak ditentukan di mana setiap implementasi mendokumentasikan bagaimana pilihan itu dibuat

2 CONTOH Contoh perilaku yang ditentukan implementasi adalah penyebaran bit orde tinggi ketika integer yang ditandatangani bergeser ke kanan.

3.4.3 1 perilaku perilaku yang tidak terdefinisi , pada saat penggunaan konstruksi program yang tidak dapat diakses atau salah atau data yang salah, yang untuknya Standar Internasional ini tidak memerlukan persyaratan

2 CATATAN Kemungkinan perilaku yang tidak terdefinisi mulai dari mengabaikan situasi sepenuhnya dengan hasil yang tidak dapat diprediksi, hingga berperilaku selama penerjemahan atau pelaksanaan program dengan cara yang terdokumentasi yang menggambarkan karakteristik lingkungan (dengan atau tanpa penerbitan pesan diagnostik), hingga penghentian terjemahan atau eksekusi (dengan penerbitan pesan diagnostik).

3 CONTOH Contoh perilaku tidak terdefinisi adalah perilaku pada integer overflow.

3.4.4 1 penggunaan perilaku yang tidak ditentukan dari nilai yang tidak ditentukan, atau perilaku lain di mana Standar Internasional ini memberikan dua atau lebih kemungkinan dan tidak memaksakan persyaratan lebih lanjut yang dipilih dalam hal apa pun

2 CONTOH Contoh perilaku yang tidak ditentukan adalah urutan di mana argumen untuk fungsi dievaluasi.

Semut
sumber
3
Apa perbedaan antara perilaku yang ditentukan dan tidak ditentukan implementasi?
Zolomon
26
@ Zolomon: Sama seperti itu mengatakan: pada dasarnya hal yang sama, kecuali bahwa dalam hal implementasi yang ditentukan implementasi diperlukan untuk mendokumentasikan (untuk menjamin) apa yang sebenarnya akan terjadi, sedangkan dalam kasus yang tidak ditentukan implementasi tidak diperlukan untuk mendokumentasikan atau menjamin apa pun.
AnT
1
@ Zolomon: Ini tercermin dalam perbedaan antara 3.4.1 dan 2.4.4.
sbi
8
@Celeritas: Compiler Hyper-modern dapat melakukan lebih baik dari itu. Mengingat int foo(int x) { if (x >= 0) launch_missiles(); return x << 1; }kompiler dapat menentukan bahwa karena semua cara menjalankan fungsi yang tidak meluncurkan rudal memanggil Perilaku Tidak Terdefinisi, itu dapat membuat panggilan ke launch_missiles()tanpa syarat.
supercat
2
@northerner Seperti yang dinyatakan dalam kutipan, perilaku yang tidak ditentukan biasanya dibatasi pada serangkaian perilaku yang mungkin terbatas. Dalam beberapa kasus Anda bahkan mungkin sampai pada kesimpulan bahwa semua kemungkinan ini dapat diterima dalam konteks yang diberikan, di mana kasus perilaku yang tidak ditentukan sama sekali bukan masalah. Perilaku tidak terdefinisi sepenuhnya tidak dibatasi (eb "program dapat memutuskan untuk memformat hard drive Anda"). Perilaku tidak terdefinisi selalu menjadi masalah.
AnT
60

Mungkin kata-kata yang mudah bisa lebih mudah untuk dipahami daripada definisi standar yang ketat.

perilaku yang ditentukan implementasi
Bahasa ini mengatakan bahwa kita memiliki tipe data. Vendor penyusun menentukan ukuran apa yang akan mereka gunakan, dan memberikan dokumentasi tentang apa yang mereka lakukan.

perilaku tidak terdefinisi
Anda melakukan sesuatu yang salah. Misalnya, Anda memiliki nilai yang sangat besar dalam suatu intyang tidak cocok char. Bagaimana Anda memasukkan nilai itu char? sebenarnya tidak mungkin! Apa pun bisa terjadi, tetapi hal yang paling masuk akal adalah mengambil byte pertama dari int itu dan memasukkannya ke dalam char. Itu hanya salah untuk melakukan itu untuk menetapkan byte pertama, tetapi itulah yang terjadi di bawah tenda.

perilaku yang tidak ditentukan
Fungsi manakah dari keduanya yang dijalankan pertama kali?

void fun(int n, int m);

int fun1()
{
  cout << "fun1";
  return 1;
}
int fun2()
{
  cout << "fun2";
  return 2;
}
...
fun(fun1(), fun2()); // which one is executed first?

Bahasa tidak menentukan evaluasi, kiri ke kanan atau kanan ke kiri! Jadi perilaku yang tidak ditentukan mungkin atau tidak dapat mengakibatkan perilaku yang tidak ditentukan, tetapi tentu saja program Anda tidak boleh menghasilkan perilaku yang tidak ditentukan.


@ eSKay Saya pikir pertanyaan Anda layak untuk mengedit jawaban untuk menjelaskan lebih banyak :)

karena fun(fun1(), fun2());bukankah perilaku "implementasi telah ditentukan"? Lagi pula, kompiler harus memilih satu atau yang lain?

Perbedaan antara implementasi yang ditentukan dan tidak ditentukan, adalah bahwa kompiler seharusnya memilih perilaku dalam kasus pertama tetapi tidak harus dalam kasus kedua. Misalnya, suatu implementasi harus memiliki satu dan hanya satu definisi sizeof(int). Jadi, tidak dapat mengatakan bahwa itu sizeof(int)adalah 4 untuk sebagian dari program dan 8 untuk yang lain. Tidak seperti perilaku yang tidak ditentukan, di mana kompiler dapat mengatakan OK saya akan mengevaluasi argumen ini dari kiri ke kanan dan argumen fungsi berikutnya dievaluasi dari kanan ke kiri. Itu bisa terjadi di program yang sama, itu sebabnya disebut tidak ditentukan . Bahkan, C ++ bisa dibuat lebih mudah jika beberapa perilaku yang tidak ditentukan ditentukan. Lihatlah di sini pada jawaban Dr. Stroustrup untuk itu :

Dikatakan bahwa perbedaan antara apa yang dapat dihasilkan memberi kompiler kebebasan ini dan membutuhkan "evaluasi kiri-ke-kanan" bisa menjadi signifikan. Saya tidak yakin, tetapi dengan kompiler yang tak terhitung banyaknya "di luar sana" mengambil keuntungan dari kebebasan dan beberapa orang dengan penuh semangat mempertahankan kebebasan itu, perubahan akan sulit dan bisa memakan waktu puluhan tahun untuk menembus ke sudut yang jauh dari dunia C dan C ++. Saya kecewa karena tidak semua kompiler memperingatkan terhadap kode seperti ++ i + i ++. Demikian pula, urutan evaluasi argumen tidak ditentukan.

IMO terlalu banyak "hal" dibiarkan tidak terdefinisi, tidak ditentukan, implementasi-didefinisikan, dll. Namun, itu mudah untuk dikatakan dan bahkan untuk memberikan contoh, tetapi sulit untuk diperbaiki. Perlu juga dicatat bahwa tidak terlalu sulit untuk menghindari sebagian besar masalah dan menghasilkan kode portabel.

AraK
sumber
1
karena fun(fun1(), fun2());bukankah perilakunya "implementation defined"? Lagi pula, kompiler harus memilih satu atau yang lain?
Lazer
1
@AraK: terima kasih atas penjelasannya. Saya mengerti sekarang. Btw, "I am gonna evaluate these arguments left-to-right and the next function's arguments are evaluated right-to-left"saya mengerti ini canterjadi. Apakah benar, dengan kompiler yang kami gunakan hari ini?
Lazer
1
@ eSKay Anda harus bertanya kepada seorang guru tentang hal ini yang membuat tangannya kotor dengan banyak kompiler :) AFAIK VC selalu mengevaluasi argumen dari kanan ke kiri.
AraK
4
@ Lazer: Itu pasti bisa terjadi. Skenario sederhana: foo (bar, boz ()) dan foo (boz (), bar), di mana bar adalah int dan boz () adalah fungsi yang mengembalikan int. Asumsikan CPU di mana parameter diharapkan akan dilewatkan di register R0-R1. Hasil fungsi dikembalikan dalam R0; fungsi dapat merusak R1. Mengevaluasi "bar" sebelum "boz ()" akan membutuhkan menyimpan salinan bar di tempat lain sebelum memanggil boz () dan kemudian memuat salinan yang disimpan itu. Mengevaluasi "bilah" setelah "boz ()" akan menghindari penyimpanan memori dan pengambilan ulang, dan merupakan pengoptimalan yang akan dilakukan banyak kompiler terlepas dari urutannya dalam daftar argumen.
supercat
6
Saya tidak tahu tentang C ++ tetapi standar C mengatakan bahwa konversi int ke char adalah implementasi yang didefinisikan atau bahkan terdefinisi dengan baik (tergantung pada nilai aktual dan penandatanganan jenis). Lihat C99 §6.3.1.3 (tidak diubah dalam C11).
Nikolai Ruhe
27

Dari Dokumen Dasar Pemikiran C resmi

Istilah perilaku yang tidak ditentukan , perilaku yang tidak terdefinisi , dan perilaku yang ditentukan implementasi digunakan untuk mengkategorikan hasil dari program penulisan yang sifat-sifatnya tidak dijelaskan oleh Standar, atau tidak dapat, sepenuhnya dijelaskan. Tujuan mengadopsi kategorisasi ini adalah untuk memungkinkan variasi tertentu di antara implementasi yang memungkinkan kualitas implementasi menjadi kekuatan aktif di pasar serta untuk memungkinkan ekstensi populer tertentu, tanpa menghapus cap kesesuaian dengan Standar. Lampiran F to the Standard mengelompokkan perilaku yang termasuk dalam salah satu dari tiga kategori ini.

Perilaku yang tidak ditentukan memberikan keleluasaan pada implementor dalam menerjemahkan program. Garis lintang ini tidak sampai gagal menerjemahkan program.

Perilaku tidak terdefinisi memberikan lisensi implementor untuk tidak menangkap kesalahan program tertentu yang sulit didiagnosis. Ini juga mengidentifikasi bidang-bidang yang kemungkinan dapat disesuaikan dengan ekstensi bahasa: implementor dapat menambah bahasa dengan memberikan definisi tentang perilaku yang tidak terdefinisi secara resmi.

Perilaku yang ditentukan implementasi memberi pelaksana kebebasan untuk memilih pendekatan yang sesuai, tetapi mengharuskan pilihan ini dijelaskan kepada pengguna. Perilaku yang ditetapkan sebagai implementasi-didefinisikan umumnya perilaku di mana pengguna dapat membuat keputusan pengkodean yang bermakna berdasarkan definisi implementasi. Pelaksana harus memperhatikan kriteria ini ketika memutuskan seberapa luas definisi implementasi seharusnya. Seperti halnya perilaku yang tidak ditentukan, gagal menerjemahkan sumber yang mengandung perilaku yang ditentukan implementasi bukan respons yang memadai.

Johannes Schaub - litb
sumber
3
Penulis kompiler hiper-modern juga menganggap "perilaku tidak terdefinisi" sebagai memberikan lisensi penulis kompiler untuk berasumsi bahwa program tidak akan pernah menerima input yang akan menyebabkan Perilaku Tidak Terdefinisi, dan untuk secara sewenang-wenang mengubah semua aspek bagaimana program berperilaku ketika mereka menerima input tersebut.
supercat
2
Poin lain yang saya perhatikan: C89 tidak menggunakan istilah "ekstensi" untuk menggambarkan fitur yang dijamin pada beberapa implementasi tetapi tidak pada yang lain. Para penulis C89 mengakui bahwa mayoritas implementasi saat itu akan memperlakukan aritmatika yang ditandatangani dan aritmatika yang tidak ditandatangani secara identik kecuali ketika hasilnya digunakan dengan cara-cara tertentu, dan perlakuan tersebut diterapkan bahkan dalam kasus limpahan yang ditandatangani; Namun, mereka tidak mencantumkannya sebagai perpanjangan umum dalam Lampiran J2, yang menunjukkan kepada saya bahwa mereka memandangnya sebagai keadaan alami, bukan sebagai perpanjangan.
supercat
10

Perilaku Tidak Terdefinisi vs Perilaku Tidak Khusus memiliki deskripsi singkat tentang itu.

Ringkasan terakhir mereka:

Singkatnya, perilaku yang tidak ditentukan biasanya merupakan sesuatu yang tidak perlu Anda khawatirkan, kecuali perangkat lunak Anda diharuskan portabel. Sebaliknya, perilaku tidak terdefinisi selalu tidak diinginkan dan tidak boleh terjadi.

Anders Abel
sumber
1
Ada dua jenis penyusun: yang penyusunnya, kecuali jika secara eksplisit didokumentasikan sebaliknya, menafsirkan sebagian besar bentuk Standar Perilaku Tidak Terdefinisi sebagai mundur dari perilaku karakteristik yang didokumentasikan oleh lingkungan yang mendasarinya, dan yang secara default hanya memaparkan perilaku-perilaku yang dicirikan oleh Standar sebagai Implementasi-Didefinisikan. Saat menggunakan kompiler dari tipe pertama, banyak hal dari tipe pertama dapat dilakukan secara efisien dan aman menggunakan UB. Kompiler untuk tipe kedua hanya akan cocok untuk tugas-tugas seperti itu jika mereka memberikan opsi untuk menjamin perilaku dalam kasus tersebut.
supercat
8

Secara historis, baik Perilaku yang Ditentukan Implementasi maupun Perilaku yang Tidak Terdefinisi mewakili situasi di mana penulis Standar berharap bahwa orang yang menulis implementasi kualitas akan menggunakan penilaian untuk memutuskan jaminan perilaku apa, jika ada, yang akan berguna untuk program dalam bidang aplikasi yang dimaksud yang berjalan pada target yang dituju. Kebutuhan kode angka-akhir tingkat tinggi sangat berbeda dari kode sistem tingkat rendah, dan baik UB maupun IDB memberikan fleksibilitas kepada penulis kompiler untuk memenuhi kebutuhan yang berbeda tersebut. Baik kategori mengamanatkan bahwa implementasi berperilaku dengan cara yang bermanfaat untuk tujuan tertentu, atau bahkan untuk tujuan apa pun. Implementasi kualitas yang mengklaim cocok untuk tujuan tertentu, bagaimanapun, harus berperilaku dengan cara yang sesuai dengan tujuan tersebutapakah Standar mengharuskannya atau tidak .

Satu-satunya perbedaan antara Perilaku yang Ditentukan Implementasi dan Perilaku yang Tidak Terdefinisi adalah bahwa yang pertama mengharuskan implementasi mendefinisikan dan mendokumentasikan perilaku yang konsisten bahkan dalam kasus di mana tidak ada implementasi yang mungkin bisa dilakukan akan berguna . Garis pemisah di antara mereka bukanlah apakah secara umum akan berguna bagi implementasi untuk mendefinisikan perilaku (penulis kompiler harus mendefinisikan perilaku yang berguna ketika praktis apakah Standar mengharuskan mereka atau tidak) tetapi apakah mungkin ada implementasi di mana mendefinisikan suatu perilaku secara simultan akan mahal. dan tidak berguna . Suatu penilaian bahwa implementasi semacam itu mungkin ada tidak dengan cara apa pun, membentuk, atau membentuk, menyiratkan penilaian apa pun tentang kegunaan mendukung perilaku yang didefinisikan pada platform lain.

Sayangnya, sejak pertengahan 1990-an penulis kompiler telah mulai menafsirkan kurangnya mandat perilaku sebagai penilaian bahwa jaminan perilaku tidak sebanding dengan biaya bahkan dalam bidang aplikasi di mana mereka sangat penting, dan bahkan pada sistem di mana mereka praktis tidak ada biaya. Alih-alih memperlakukan UB sebagai undangan untuk melakukan penilaian yang masuk akal, penulis kompiler mulai memperlakukannya sebagai alasan untuk tidak melakukannya.

Misalnya diberi kode berikut:

int scaled_velocity(int v, unsigned char pow)
{
  if (v > 250)
    v = 250;
  if (v < -250)
    v = -250;
  return v << pow;
}

implementasi dua-pelengkap tidak perlu mengeluarkan upaya apa pun untuk memperlakukan ekspresi v << powsebagai pergeseran dua-pelengkap tanpa memperhatikan apakah vitu positif atau negatif.

Filosofi yang lebih disukai di antara beberapa penulis kompiler hari ini, bagaimanapun, akan menyarankan bahwa karena vhanya bisa negatif jika program akan terlibat dalam Perilaku Tidak Terdefinisi, tidak ada alasan untuk memiliki program klip kisaran negatif v. Meskipun pergeseran nilai-nilai negatif yang dulunya didukung pada setiap penyusun signifikansi tunggal, dan sejumlah besar kode yang ada bergantung pada perilaku itu, filsafat modern akan menafsirkan fakta bahwa Standar mengatakan bahwa nilai-nilai negatif pergeseran-kiri adalah UB sebagai menyiratkan bahwa penulis kompiler harus merasa bebas untuk mengabaikannya.

supercat
sumber
Tetapi menangani perilaku tidak terdefinisi dengan cara yang baik tidak datang secara gratis. Seluruh alasan bahwa kompiler modern menunjukkan perilaku aneh dalam beberapa kasus di UB adalah bahwa mereka mengoptimalkan tanpa henti, dan untuk melakukan pekerjaan terbaik pada saat itu, mereka harus dapat mengasumsikan bahwa UB tidak pernah terjadi.
Tom Swirly
Tetapi fakta bahwa <<UB pada angka negatif adalah jebakan kecil yang tidak menyenangkan dan saya senang diingatkan akan hal itu!
Tom Swirly
1
@ TomSwirly: Sayangnya, penulis kompiler tidak peduli bahwa menawarkan jaminan perilaku yang longgar di luar yang diamanatkan oleh Standar sering dapat memungkinkan peningkatan kecepatan besar-besaran dibandingkan dengan mengharuskan kode tersebut menghindari semua biaya apa pun yang tidak didefinisikan oleh Standar. Jika seorang programmer tidak peduli apakah i+j>kmenghasilkan 1 atau 0 dalam kasus di mana penambahan melimpah, asalkan tidak memiliki efek samping lain , kompiler mungkin dapat membuat beberapa optimasi besar yang tidak akan mungkin jika programmer menulis kode sebagai (int)((unsigned)i+j) > k.
supercat
1
@ TomSwirly: Bagi mereka, jika kompiler X dapat mengambil program yang benar-benar sesuai untuk melakukan beberapa tugas T dan menghasilkan executable yang 5% lebih efisien daripada kompiler Y akan menghasilkan dengan program yang sama, itu berarti X lebih baik, bahkan jika Y dapat menghasilkan kode yang melakukan tugas yang sama tiga kali lebih efisien mengingat program yang mengeksploitasi perilaku yang dijamin Y tetapi X tidak.
supercat
6

C ++ standar n3337 § 1.3.10 perilaku yang ditentukan implementasi

perilaku, untuk program yang dibentuk dengan baik membangun dan mengoreksi data, yang tergantung pada implementasi dan bahwa setiap dokumen implementasi

Kadang-kadang C ++ Standard tidak memaksakan perilaku tertentu pada beberapa konstruksi tetapi sebaliknya mengatakan bahwa perilaku tertentu, yang didefinisikan dengan baik harus dipilih dan dijelaskan oleh implementasi tertentu (versi perpustakaan). Jadi pengguna masih bisa tahu persis bagaimana program akan berperilaku meskipun Standar tidak menggambarkan ini.


C ++ standar n3337 § 1.3.24 perilaku tidak terdefinisi

perilaku dimana Standar Internasional ini tidak memaksakan persyaratan [Catatan: Perilaku tidak terdefinisi dapat diharapkan ketika Standar Internasional ini menghilangkan definisi perilaku yang eksplisit atau ketika suatu program menggunakan konstruksi yang salah atau data yang salah. Perilaku yang tidak terdefinisi yang diizinkan berkisar dari mengabaikan situasi sepenuhnya dengan hasil yang tidak terduga, hingga berperilaku selama penerjemahan atau pelaksanaan program dengan cara yang terdokumentasi sebagai karakteristik lingkungan (dengan atau tanpa penerbitan pesan diagnostik), hingga penghentian terjemahan atau eksekusi (dengan penerbitan pesan diagnostik). Banyak konstruksi program yang salah tidak menimbulkan perilaku tidak terdefinisi; mereka harus didiagnosis. - catatan akhir]

Ketika program menemukan konstruk yang tidak didefinisikan menurut C ++ Standard, ia diizinkan untuk melakukan apa pun yang ingin dilakukannya (mungkin mengirim email kepada saya atau mungkin mengirim email kepada Anda atau mungkin mengabaikan kode sepenuhnya).


C ++ standar n3337 § 1.3.25 perilaku yang tidak ditentukan

perilaku, untuk konstruksi program yang dibentuk dengan baik dan data yang benar, itu tergantung pada implementasi [Catatan: Implementasi tidak diperlukan untuk mendokumentasikan perilaku mana yang terjadi. Berbagai perilaku yang mungkin biasanya digambarkan oleh Standar Internasional ini. - catatan akhir]

C ++ Standard tidak memaksakan perilaku tertentu pada beberapa konstruksi tetapi sebaliknya mengatakan bahwa perilaku tertentu, yang didefinisikan dengan baik harus dipilih ( tidak perlu dijelaskan ) dengan implementasi tertentu (versi perpustakaan). Jadi dalam kasus ketika tidak ada deskripsi yang diberikan, mungkin sulit bagi pengguna untuk mengetahui dengan tepat bagaimana program akan berperilaku.

4pie0
sumber
6

Implementasi didefinisikan-

Pelaksana berharap, harus didokumentasikan dengan baik, standar memberikan pilihan tetapi yakin untuk dikompilasi

Tidak ditentukan -

Sama seperti implementasi yang ditentukan tetapi tidak didokumentasikan

Tidak terdefinisi-

Apa pun bisa terjadi, rawatlah.

Suraj K Thomas
sumber
2
Saya pikir penting untuk dicatat bahwa arti praktis dari "tidak terdefinisi" telah berubah selama beberapa tahun terakhir. Dulu diberikan uint32_t s;, mengevaluasi 1u<<skapan s33 dapat diharapkan untuk menghasilkan 0 atau mungkin menghasilkan 2, tetapi tidak melakukan hal lain yang aneh. Kompiler yang lebih baru, bagaimanapun, mengevaluasi 1u<<sdapat menyebabkan kompiler menentukan bahwa karena sharus kurang dari 32 sebelumnya, kode apa pun sebelum atau setelah ekspresi yang hanya akan relevan jika stelah 32 atau lebih besar dapat dihilangkan.
supercat