pengembangan-web-mp.com

Mengapa operator TIDAK logis dalam bahasa gaya-C "!" dan bukan "~~"?

Untuk operator biner, kami memiliki operator bitwise dan logis:

& bitwise AND
| bitwise OR

&& logical AND
|| logical OR

BUKAN (operator unary) berperilaku berbeda. Ada ~ untuk bitwise dan! untuk logika.

Saya mengenali NOT adalah operasi unary yang bertentangan dengan AND dan OR tetapi saya tidak dapat memikirkan alasan mengapa para perancang memilih untuk menyimpang dari prinsip bahwa single bitwise dan double logis di sini, dan pergi untuk karakter yang berbeda sebagai gantinya. Saya kira Anda bisa membacanya salah, seperti operasi bitwise ganda yang akan selalu mengembalikan nilai operan. Tapi itu sepertinya bukan masalah bagi saya.

Apakah ada alasan saya hilang?

42
Martin Maat

Anehnya, sejarah bahasa pemrograman C-style tidak dimulai dengan C.

Dennis Ritchie menjelaskan dengan baik tantangan kelahiran C di artikel ini .

Ketika membacanya, menjadi jelas bahwa C mewarisi bagian dari desain bahasanya dari pendahulunya BCPL , dan terutama operator. Bagian “Neonatal C” dari artikel tersebut menjelaskan bagaimana BCPL & Dan | Diperkaya dengan dua operator baru && Dan ||. Alasannya adalah:

  • diperlukan perbedaan prioritas karena penggunaannya dalam kombinasi dengan ==
  • logika evaluasi yang berbeda: evaluasi kiri-ke-kanan dengan hubung singkat (yaitu ketika a adalah false dalam a&&b, b tidak dievaluasi).

Menariknya, penggandaan ini tidak menciptakan ambiguitas bagi pembaca: a && b Tidak akan disalahartikan sebagai a(&(&b)). Dari sudut pandang parsing, tidak ada ambiguitas: &b Bisa masuk akal jika b adalah nilai lvalu, tetapi itu akan menjadi sebuah pointer sedangkan bitwise & Akan membutuhkan operan integer, sehingga logika DAN akan menjadi satu-satunya pilihan yang masuk akal.

BCPL sudah menggunakan ~ Untuk negasi bitwise. Jadi dari sudut pandang konsistensi, bisa digandakan untuk memberikan ~~ Untuk memberikan arti logisnya. Sayangnya ini akan menjadi sangat ambigu karena ~ Adalah operator yang tidak disadari: ~~b Juga bisa berarti ~(~b)). Inilah sebabnya mengapa simbol lain harus dipilih untuk negasi yang hilang.

110
Christophe

Saya tidak dapat memikirkan alasan mengapa para desainer memilih untuk menyimpang dari prinsip bahwa tunggal itu bitwise dan double adalah logis di sini,

Itu bukan prinsip di tempat pertama; begitu Anda menyadarinya, itu lebih masuk akal.

Cara yang lebih baik untuk memikirkan & vs && bukan binary dan Boolean. Cara yang lebih baik adalah menganggap mereka sebagai bersemangat dan malas. & operator mengeksekusi sisi kiri dan kanan lalu menghitung hasilnya. && operator mengeksekusi sisi kiri, dan kemudian mengeksekusi sisi kanan hanya jika perlu untuk menghitung hasilnya.

Selain itu, alih-alih berpikir tentang "biner" dan "Boolean", pikirkan tentang apa yang sebenarnya terjadi. Versi "biner" hanyalah melakukan operasi Boolean pada array Boolean yang telah dikemas ke dalam Word.

Jadi mari kita kumpulkan. Apakah masuk akal untuk melakukan operasi malas pada array Boolean? Tidak, karena tidak ada "sisi kiri" untuk diperiksa terlebih dahulu. Ada 32 "sisi kiri" untuk diperiksa terlebih dahulu. Jadi kami membatasi operasi malas ke tunggal Boolean, dan di situlah intuisi Anda bahwa salah satunya adalah "biner" dan satu adalah "Boolean" berasal, tetapi itu adalah konsekuensi dari desain, bukan desain itu sendiri!

Dan ketika Anda memikirkannya seperti itu, menjadi jelas mengapa tidak ada !! dan tidak ^^. Tak satu pun dari operator tersebut memiliki properti yang dapat Anda lewati saat menganalisis salah satu operan; tidak ada "malas" not atau xor.

Bahasa lain membuatnya lebih jelas; beberapa bahasa menggunakan and yang berarti "bersemangat dan" tetapi and also berarti "malas dan", misalnya. Dan bahasa lain juga membuatnya lebih jelas bahwa & dan && bukan "biner" dan "Boolean"; misalnya dalam C #, kedua versi dapat menggunakan Boolean sebagai operan.

51
Eric Lippert

TL; DR

C mewarisi ! dan ~ operator dari bahasa lain. Keduanya && dan || ditambahkan bertahun-tahun kemudian oleh orang yang berbeda.

Jawaban panjang

Secara historis, C dikembangkan dari bahasa awal B, yang didasarkan pada BCPL, yang didasarkan pada CPL, yang didasarkan pada ALGOL.

ALGOL , kakek buyut C++, Java dan C #, didefinisikan benar dan salah dengan cara yang kemudian terasa intuitif untuk programmer: "nilai kebenaran yang, dianggap sebagai bilangan biner (benar sesuai dengan 1 dan salah ke 0), adalah sama dengan nilai integral intrinsik". Namun, satu kelemahan dari ini adalah bahwa logis dan bitwise tidak dapat menjadi operasi yang sama: Pada komputer modern apa pun, ~0 sama dengan -1 daripada 1 dan ~1 sama dengan -2 daripada 0. (Bahkan pada mainframe berusia enam puluh tahun di mana ~0 mewakili -0 atau INT_MIN, ~0 != 1 pada setiap CPU yang pernah dibuat, dan standar bahasa C telah mengharuskannya selama bertahun-tahun, sementara sebagian besar bahasa putrinya bahkan tidak repot-repot mendukung tanda-dan-besarnya atau pelengkap satu sama sekali.)

ALGOL mengatasi ini dengan memiliki mode yang berbeda dan menafsirkan operator berbeda dalam mode boolean dan integral. Yaitu, operasi bitwise adalah pada tipe integer, dan operasi logis adalah pada tipe boolean.

BCPL memiliki tipe boolean yang terpisah, tetapi operator not tunggal , untuk bitwise dan logical tidak. Cara cikal bakal awal C ini bekerja adalah:

Nilai dari true adalah pola bit yang seluruhnya terdiri dari yang; nilai salah adalah nol.

Perhatikan bahwa true = ~ false

(Anda akan mengamati bahwa istilah rvalue telah berevolusi menjadi sesuatu yang sama sekali berbeda dalam bahasa keluarga C. Kita hari ini akan menyebutnya "representasi objek" dalam C.)

Definisi ini akan memungkinkan logis dan bitwise untuk tidak menggunakan instruksi bahasa mesin yang sama. Jika C telah menempuh rute itu, file header di seluruh dunia akan mengatakan #define TRUE -1.

Tapi bahasa pemrograman B diketik dengan lemah, dan tidak memiliki tipe boolean atau bahkan floating-point. Semuanya setara dengan int dalam penggantinya, C. Ini membuatnya menjadi ide yang bagus untuk bahasa untuk menentukan apa yang terjadi ketika suatu program menggunakan nilai selain benar atau salah sebagai nilai logis. Pertama-tama didefinisikan ekspresi yang benar sebagai "tidak sama dengan nol." Ini efisien pada minicomputer yang menjalankannya, yang memiliki flag nol CPU.

Ada, pada saat itu, sebuah alternatif: CPU yang sama juga memiliki tanda negatif, dan nilai kebenaran BCPL adalah -1, jadi B mungkin telah mendefinisikan semua angka negatif sebagai benar dan semua angka non-negatif sebagai kepalsuan. (Ada satu sisa dari pendekatan ini: UNIX, yang dikembangkan oleh orang yang sama pada saat yang sama, mendefinisikan semua kode kesalahan sebagai bilangan bulat negatif. Banyak panggilan sistemnya mengembalikan salah satu dari beberapa nilai negatif yang berbeda pada kegagalan.) Jadi bersyukurlah: bisa lebih buruk!

Tetapi mendefinisikan TRUE sebagai 1 dan FALSE sebagai 0 dalam B berarti identitas true = ~ false tidak lagi ditahan, dan telah menjatuhkan pengetikan yang kuat yang memungkinkan ALGOL untuk membedakan antara bitwise dan ekspresi logis. Itu membutuhkan operator logis-bukan yang baru, dan desainer memilih !, mungkin karena sudah tidak sama dengan sudah !=, yang terlihat seperti bilah vertikal melalui tanda sama dengan. Mereka tidak mengikuti konvensi yang sama dengan && atau || karena belum ada yang ada.

Boleh dibilang, mereka harus memiliki: the & operator di B rusak seperti yang dirancang. Di B dan di C, 1 & 2 == FALSE meskipun 1 dan 2 keduanya adalah nilai kebenaran, dan tidak ada cara intuitif untuk mengekspresikan operasi logis dalam B. Itu adalah satu kesalahan yang dicoba C sebagian untuk diperbaiki dengan menambahkan && dan ||, tetapi perhatian utama pada saat itu adalah akhirnya membuat hubungan arus pendek bekerja, dan membuat program berjalan lebih cepat. Buktinya adalah tidak ada ^^: 1 ^ 2 adalah nilai yang benar meskipun kedua operannya benar, tetapi tidak dapat memanfaatkan hubungan arus pendek.

22
Davislor