Untuk operator biner, kami memiliki operator bitwise dan logical:
& bitwise AND
| bitwise OR
&& logical AND
|| logical OR
BUKAN (operator unary) berperilaku berbeda. Ada ~ untuk bitwise dan! untuk logika.
Saya mengenali NOT adalah operasi yang tidak disadari sebagai lawan dari AND dan ATAU tetapi saya tidak dapat memikirkan alasan mengapa para perancang memilih untuk menyimpang dari prinsip bahwa single bitwise dan double logis di sini, dan sebagai gantinya memilih karakter yang berbeda. 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?
~~
kemudian tidak lebih konsisten untuk logika NOT, jika Anda mengikuti pola yang operator logis adalah dua kali lipat dari operator bitwise?!!foo
adalah idiom yang tidak biasa (tidak tidak umum?). Ini menormalkan argumen nol atau bukan nol untuk0
atau1
.Jawaban:
Anehnya, sejarah bahasa pemrograman C-style tidak dimulai dengan C.
Dennis Ritchie menjelaskan dengan baik tantangan kelahiran C dalam artikel ini .
Ketika membacanya, menjadi jelas bahwa C mewarisi bagian dari desain bahasanya dari BCPL pendahulunya , dan terutama operator. Bagian “Neonatal C” dari artikel tersebut menjelaskan bagaimana BCPL
&
dan|
diperkaya dengan dua operator baru&&
dan||
. Alasannya adalah:==
a
adalahfalse
dia&&b
,b
tidak dievaluasi).Menariknya, penggandaan ini tidak menciptakan ambiguitas bagi pembaca:
a && b
tidak akan disalahartikan sebagaia(&(&b))
. Dari sudut pandang parsing, tidak ada ambiguitas baik:&b
bisa masuk akal jikab
merupakan nilai, tetapi itu akan menjadi pointer sedangkan bitwise&
akan membutuhkan operan integer, sehingga logika DAN akan menjadi satu-satunya pilihan yang masuk akal.BCPL sudah digunakan
~
untuk negasi bitwise. Jadi dari sudut pandang konsistensi, bisa digandakan untuk memberikan~~
arti logis. Sayangnya ini akan menjadi sangat ambigu karena~
merupakan operator unary:~~b
bisa juga berarti~(~b))
. Inilah sebabnya mengapa simbol lain harus dipilih untuk negasi yang hilang.sumber
(t)+1
apakah itu merupakan tambahan(t)
dan1
atau apakah itu pemeran+1
untuk mengetikt
? Desain C ++ harus menyelesaikan masalah bagaimana lex template yang berisi>>
dengan benar. Dan seterusnya.&&
sebagai&&
token tunggal dan bukan sebagai dua&
token, karenaa & (&b)
penafsirannya bukan hal yang masuk akal untuk ditulis, sehingga manusia tidak akan pernah bermaksud dan terkejut oleh kompiler memperlakukannya sebagaia && b
. Sedangkan keduanya!(!a)
dan!!a
merupakan hal-hal yang mungkin bagi manusia untuk diartikan, jadi itu adalah ide yang buruk bagi kompiler untuk menyelesaikan ambiguitas dengan aturan level tokenization yang sewenang-wenang.!!
tidak hanya mungkin / masuk akal untuk menulis, tetapi idiom kanonik "convert to boolean".--a
vs-(-a)
, keduanya valid secara sintaksis tetapi memiliki semantik yang berbeda.Itu bukan prinsip di tempat pertama; begitu Anda menyadarinya, itu lebih masuk akal.
Cara yang lebih baik untuk memikirkan
&
vs&&
bukan biner dan Boolean . Cara yang lebih baik adalah menganggap mereka bersemangat dan malas . The&
Operator mengeksekusi sisi kiri dan kanan dan kemudian menghitung hasilnya. The&&
Operator mengeksekusi sisi kiri, dan kemudian mengeksekusi sisi kanan hanya jika diperlukan untuk menghitung hasilnya.Selain itu, alih-alih berpikir tentang "biner" dan "Boolean", pikirkan tentang apa yang sebenarnya terjadi. Versi "biner" hanya melakukan operasi Boolean pada array Boolean yang telah dikemas menjadi sebuah kata .
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 satu Boolean, dan dari 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
atauxor
.Bahasa lain membuatnya lebih jelas; beberapa bahasa digunakan
and
untuk berarti "bersemangat dan" tetapiand also
berarti "malas dan", misalnya. Dan bahasa lain juga membuatnya lebih jelas&
dan&&
bukan "biner" dan "Boolean"; misalnya dalam C #, kedua versi dapat menggunakan Boolean sebagai operan.sumber
&
dan&&
. Sementara eagerness adalah salah satu perbedaan antara&
dan&&
,&
berperilaku sangat berbeda dari versi yang bersemangat&&
, terutama dalam bahasa di mana&&
mendukung jenis selain jenis boolean khusus.1 & 2
memiliki hasil yang sama sekali berbeda dari1 && 2
.bool
tipe C memiliki efek knock-on. Kita membutuhkan keduanya!
dan~
karena satu berarti "memperlakukan int sebagai Boolean tunggal" dan satu berarti "memperlakukan int sebagai array Boolean yang dikemas". Jika Anda memiliki tipe bool dan int yang terpisah maka Anda dapat memiliki hanya satu operator, yang menurut saya akan menjadi desain yang lebih baik, tapi kami hampir terlambat 50 tahun pada operator itu. C # mempertahankan desain ini agar tidak asing.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 #, mendefinisikan benar dan salah dengan cara yang terasa intuitif bagi para programmer: "nilai kebenaran yang, dianggap sebagai angka 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 tidak bisa menjadi operasi yang sama: Pada komputer modern apa pun,
~0
sama dengan -1 daripada 1 dan~1
sama dengan -2 daripada 0. (Bahkan pada beberapa mainframe berusia enam puluh tahun di mana~0
mewakili - 0 atauINT_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 komplemen seseorang sama sekali.)Algol mengatasi ini dengan memiliki mode 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 satu
not
operator , untuk bitwise dan logical tidak. Cara cikal bakal awal C ini bekerja adalah:(Anda akan mengamati bahwa istilah nilai p telah berkembang berarti sesuatu yang sama sekali berbeda dalam bahasa C-keluarga. Kami akan hari ini panggilan itu “objek representasi” di 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
.Tetapi bahasa pemrograman B diketik dengan lemah, dan tidak memiliki tipe boolean atau bahkan floating-point. Semuanya setara
int
dengan penggantinya, C. Ini membuatnya menjadi ide yang baik bagi bahasa untuk mendefinisikan 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 tempat ia berlari, yang memiliki flag nol CPU.Ada, pada saat itu, sebuah alternatif: CPU yang sama juga memiliki flag 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 kegagalan yang berbeda.) Jadi bersyukurlah: bisa lebih buruk!
Tetapi mendefinisikan
TRUE
sebagai1
danFALSE
seperti0
dalam B berarti bahwa identitastrue = ~ false
tidak lagi dipegang, dan itu telah menghilangkan ketikan yang kuat yang memungkinkan Algol untuk ambigu antara ekspresi bitwise dan logis. Untuk itu diperlukan operator logis-bukan yang baru, dan desainer memilih!
, mungkin karena sudah tidak sama dengan yang sudah!=
, yang terlihat seperti bar vertikal melalui tanda sama. Mereka tidak mengikuti konvensi yang sama dengan&&
atau||
karena belum ada satu pun.Boleh dibilang, mereka harus memiliki:
&
operator di B rusak seperti yang dirancang. Di B dan di C,1 & 2 == FALSE
meskipun1
dan2
keduanya adalah nilai-nilai yang benar, dan tidak ada cara intuitif untuk mengekspresikan operasi logis dalam B. Itu adalah satu kesalahan C mencoba untuk memperbaiki sebagian dengan menambahkan&&
dan||
, tetapi perhatian utama pada saat itu adalah untuk akhirnya mendapatkan hubungan arus pendek, dan membuat program berjalan lebih cepat. Buktinya adalah bahwa tidak ada^^
:1 ^ 2
adalah nilai yang benar meskipun kedua operannya benar, tetapi tidak dapat mengambil manfaat dari hubungan arus pendek.sumber
~0
(semua bit diatur) adalah nol komplemen negatif seseorang (atau representasi perangkap). Tanda / magnitudo~0
adalah angka negatif dengan magnitudo maksimum.