Mengapa ini jika-pernyataan menggabungkan tugas dan pemeriksaan kesetaraan kembali benar?

216

Saya telah memikirkan beberapa kesalahan pemula dan saya berakhir dengan satu di ifpernyataan itu. Saya memperluas sedikit kode untuk ini:

int i = 0;
if (i = 1 && i == 0) {
    std::cout << i;
}

Saya telah melihat bahwa ifpernyataan pengembalian yang benar, dan itu cout's isebagai 1. Jika iditugaskan 1dalam pernyataan if, mengapa i == 0kembali true?

TehMattGR
sumber
83
Kawan, ini bukan pertanyaan salah ketik. OP ingin tahu mengapa pernyataan if dimasukkan dengan kode ini karena idiatur ke 1.
NathanOliver
24
Atau apakah itu menetapkan hasil 1 && i == 0?
JVApen
4
Saran untuk pemula: Mereka tidak boleh menggunakan konstruksi bahasa "maju" seperti itu. Cukup tetapkan variabel secara terpisah. Itu juga akan menghindari kemungkinan masalah dengan titik urutan. Kode semacam ini dalam kode praktis biasanya akan terlihat buruk juga.
user202729
1
ini pasti berakhir pada pertanyaan wawancara
RAZ_Muh_Taz

Jawaban:

391

Ini ada hubungannya dengan prioritas operator .

if (i = 1 && i == 0)

tidak

if ((i = 1) && (i == 0))

karena keduanya &&dan ==memiliki prioritas lebih tinggi daripada =. Apa yang benar-benar berhasil adalah

if (i = (1 && (i == 0)))

yang menetapkan hasil 1 && (i == 0)untuk i. Jadi, jika imulai saat 0itu i == 0adalah true, begitu 1 && truejuga true(atau 1), dan kemudian idiatur ke 1. Maka sejak 1itu benar, Anda memasukkan blok if dan mencetak nilai yang Anda tetapkan i.

NathanOliver
sumber
2
@ JörgWMittag Itu sangat keren. Saya suka itu memaksa Anda untuk menggunakan tanda kurung.
NathanOliver
Pada dasarnya, i = !i; if (i)ditulis dengan benar
Cacahuete Frito
@NathanOliver: Benteng adalah bahasa yang cukup keren yang membuat banyak hal benar. (Perancang utama adalah Guy L. Steele, jadi tidak mengherankan di sana.) Sayangnya, itu tidak diambil untuk putaran terakhir pendanaan DARPA, dan kemudian digerogoti oleh Oracle.
Jörg W Mittag
6
Secara alami, meminta sedikit peringatan dari kompiler akan mendeteksi kesalahan itu,
Deduplicator
4
Bahasa apa pun yang tidak menganggap int dan boolean setara juga akan mengambil ini.
rghome
16

Dengan asumsi kode Anda benar-benar terlihat seperti ini:

#include <iostream>
using namespace std;

int main()  {
    int i = 0;
    if (i = 1 && i == 0) {
        cout << i;
    }
}

Lalu ini:

if (i = 1 && i == 0) {

mengevaluasi sebagai

 if (i = (1 && i == 0)) {

dan idiatur ke 1.


sumber
39
Apakah kode tambahan benar-benar diperlukan? Tampaknya cukup jelas bahwa ini akan menjadi kasus karena tidak akan berjalan sebaliknya.
Modelmat
13
Bukan hanya kode tambahan yang tidak perlu. Jawabannya gagal menjelaskan dengan jelas prioritas operator.
Francisco Zarabozo
34
Karena kita berada di kereta nitpick ... Saya melihat using namespace std!
Mateen Ulhaq
8
Ada kode tambahan - tetapi masih bukan kode yang salah. Dan jawabannya masih benar. Tentu saja, itu tidak menjelaskan prioritas operator. Tetapi seseorang dapat menyarankannya untuk ditambahkan, bukannya langsung memilih!
B Charles H
14
Wow, -4 keras, mengingat ini menjawab pertanyaan dengan benar, meskipun mungkin tidak optimal. Itu tidak berkembang pada operator diutamakan sebanyak jawaban lainnya, tetapi ia mengatakan cukup tentang hal itu dalam konteks kode sehingga siapa pun yang berpikir yang =datang sebelum &&dapat melihat masalah. Juga, ya, ekspansi itu asing, tapi saya pikir itu tidak penting. Saya tidak percaya perbedaan kecil seperti itu menyebabkan orang memilih 151 hingga -4.
JoL
-4

Itu ada hubungannya dengan parsing hak untuk meninggalkan aturan. Misal y = x + 5.
Semua sub-ekspresi diberi bobot penting. Dua ekspresi sama pentingnya dievaluasi dari kanan ke kiri,. Sisi && ekspresi dilakukan terlebih dahulu, diikuti oleh LHS.

Masuk akal bagi saya.

Leslie Satenstein
sumber
1
Associativity ("aturan kanan ke kiri") tidak ada hubungannya dengan ini. Ini tentang prioritas ("kepentingan"), dan operator yang digunakan tidak memiliki prioritas yang sama.
Lightness Races in Orbit
-4

Jawaban sebenarnya adalah:

  1. Kompiler memberikan prioritas pada "i == 0", yang dievaluasi menjadi true.
  2. Kemudian akan mengevaluasi i = 1 sebagai TRUE atau FALSE, dan karena operator penugasan yang dikompilasi tidak pernah gagal (jika tidak mereka tidak akan mengkompilasi), ia juga mengevaluasi ke true.
  3. Karena kedua pernyataan dievaluasi sebagai benar, dan TRUE && TRUE mengevaluasi ke TRUE, pernyataan jika akan mengevaluasi ke TRUE.

Sebagai bukti, lihat saja asm output dari kompiler Anda untuk kode yang Anda masukkan (semua komentar adalah milik saya):

mov     dword ptr [rbp - 8], 0    ; i = 0;
cmp     dword ptr [rbp - 8], 0    ; i == 0?
sete    al                        ; TRUE (=1)
mov     cl, al
and     cl, 1                     ; = operator always TRUE
movzx   edx, cl
mov     dword ptr [rbp - 8], edx  ; set i=TRUE;
test    al, 1                     ; al never changed,
                                  ; so final ans is TRUE

Output asm di atas berasal dari CLANG, tetapi semua kompiler lain yang saya lihat memberikan hasil yang serupa. Ini berlaku untuk semua kompiler di situs itu, apakah mereka adalah kompiler C murni atau C ++, semua tanpa pragma untuk mengubah mode kompiler (yang secara default adalah C ++ untuk kompiler C ++)

Perhatikan bahwa kompiler Anda tidak benar-benar mengatur i = 1, tetapi i = BENAR (yang berarti 32-bit bukan nilai integer nol). Itu karena operator && hanya mengevaluasi apakah pernyataan itu BENAR atau SALAH, dan kemudian menetapkan hasil sesuai dengan hasil itu. Sebagai bukti, coba ubah i = 1 ke i = 2 dan Anda dapat mengamati sendiri bahwa tidak ada yang akan berubah. Lihat sendiri menggunakan kompiler online di Compiler Explorer

ar18
sumber
1
1) Tautan dokumen ke prioritas operator C, ketika pertanyaan ini ditandai dengan C ++. Dua bahasa berbeda. 2a) i = 1adalah operator penugasan [bukan kesetaraan]; 2b) Saya dapat meyakinkan Anda bahwa if (i = 0)akan mengevaluasi ke kondisi salah di C dan C ++, jadi apakah itu mengevaluasi ke true WRT "itu tidak pernah gagal" agak menyesatkan.
TrebledJ
1
and cl, 1 ; = operator always TRUE<< koreksi saya jika saya salah, tapi saya tidak melihat tugas di sini. Ini mewakili 1 &&bagian dari ekspresi. Jadi jawaban ini pada dasarnya mengevaluasi false.
syck
"Tautan dokumen ke prioritas operator C, ketika pertanyaan ini ditandai dengan C ++. Dua bahasa berbeda" - dan ketika Anda membandingkan prioritas operator C dan C ++, apa perbedaan antara keduanya? Mereka memiliki prioritas yang sama dalam hal topik ini, yang tidak mengejutkan, mengingat C ++ adalah turunan langsung dari C (atau cara lain untuk menjelaskannya adalah, C adalah bagian dari bahasa C ++, jadi tentu saja mereka akan memiliki banyak kesamaan, termasuk diutamakan). Saya akan memperbaiki posting saya, kalau-kalau membingungkan.
ar18
"Koreksi saya jika saya salah, tetapi saya tidak melihat tugas di sini" - Lalu biarkan saya memperbaiki Anda! Angka 1 adalah nilai langsung dan bukan hasil dari tes atau perhitungan apa pun. Inilah yang disebut sebagai nilai "yang dianggap BENAR". Satu-satunya tes yang terjadi adalah untuk pernyataan i == 0, yaitu - "cmp dword ptr [rbp - 8], 0". Anda hanya akan benar jika mengatakan "movzx edx, 1". Menurut SEMUA posting sebelumnya tambang, harus ada dua perbandingan, tetapi dalam kehidupan nyata hanya ada satu, dan output AS SETIAP kompiler utama membuktikan posting tersebut benar-benar salah.
ar18
2
Selain mendapatkan presedensi yang salah (lihat jawaban NathanOliver untuk penguraian yang benar), Anda membuat klaim palsu bahwa operator penugasan selalu mengevaluasi ke TRUE. Coba if ( i = 0 ) { print something }. Juga jawaban Anda bertentangan dengan dirinya sendiri; di awal Anda mengatakan bahwa i=1dievaluasi sebelum &&diterapkan, dan kemudian pada akhirnya Anda mengatakan bahwa idiatur ke hasil &&operator.
MM