+ (+ k--) ekspresi dalam C

9

Saya melihat pertanyaan ini dalam sebuah tes di mana kita harus memberi tahu keluaran kode berikut.

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
    k=k++;
    printf("%d\n", k);  
    return 0;
}

Outputnya adalah -1. Saya tidak yakin mengapa ini jawabannya.

Apa arti ungkapan +(+k--)dalam C?

Ankur Gautam
sumber
4
Apakah sudah diformat seperti ini? Itu berarti ;-)
Peter - Pasang kembali Monica
3
Petunjuk: jangan pernah menulis kode BS seperti ini di kehidupan nyata.
Jabberwocky
5
Ini bukan UB. Lihat jawaban saya.
dbush
@ Peter-ReinstateMonica Ya itu diformat seperti ini.
Ankur Gautam
2
Karena sifatnya yang dibuat-buat dan liku yang aneh - k=k++tidak terdefinisi, tetapi tidak terdefinisi karena tidak pernah dieksekusi karena kondisi yang dikaburkan - Saya memilih untuk menutup sebagai duplikat dari pertanyaan ini .
Steve Summit

Jawaban:

10

[Sebagai catatan, saya telah mengedit jawaban ini dengan cukup signifikan sejak diterima dan dipilih. Tapi pada dasarnya masih mengatakan hal yang sama.]

Kode ini sangat, mungkin sengaja, membingungkan. Ini berisi contoh sempit dari perilaku yang tidak terdefinisi ketakutan . Pada dasarnya tidak mungkin untuk menentukan apakah orang yang menyusun pertanyaan ini sangat, sangat pintar atau sangat, sangat bodoh. Dan "pelajaran" kode ini mungkin dimaksudkan untuk mengajarkan atau menanyai Anda tentang - yaitu, bahwa operator plus unary tidak berbuat banyak - tentu saja tidak cukup penting untuk pantas mendapatkan jenis penyesatan subversif semacam ini.

Ada dua aspek kode yang membingungkan, kondisi aneh:

while(+(+k--)!=0)

dan pernyataan gila yang dikontrolnya:

k=k++;

Saya akan membahas bagian kedua terlebih dahulu.

Jika Anda memiliki variabel seperti kyang ingin Anda tambahkan 1, C memberi Anda bukan satu, bukan dua, bukan tiga, tetapi empat cara berbeda untuk melakukannya:

  1. k = k + 1
  2. k += 1
  3. ++k
  4. k++

Terlepas dari karunia ini (atau mungkin karena itu), beberapa programmer menjadi bingung dan batuk seperti contortions

k = k++;

Jika Anda tidak tahu apa yang harus dilakukan, jangan khawatir: tidak ada yang bisa. Ungkapan ini berisi dua upaya berbeda untuk mengubah knilai ( k =bagian, dan k++bagian), dan karena tidak ada aturan dalam C untuk mengatakan mana dari percobaan modifikasi yang "menang", ungkapan seperti ini secara resmi tidak ditentukan , artinya tidak hanya itu telah ada didefinisikan arti, tetapi bahwa seluruh program yang berisi itu adalah tersangka.

Sekarang, jika Anda melihat dengan sangat hati-hati, Anda akan melihat bahwa dalam program khusus ini, garis k = k++tidak benar-benar dieksekusi, karena (seperti yang akan kita lihat) kondisi pengendalian pada awalnya salah, sehingga loop berjalan 0 kali . Jadi program khusus ini mungkin sebenarnya tidak terdefinisi - tetapi masih membingungkan secara patologis.

Lihat juga jawaban SO kanonik ini untuk semua pertanyaan tentang Perilaku Tidak Terdefinisi dari jenis ini.

Tetapi Anda tidak bertanya tentang k=k++bagian itu. Anda bertanya tentang bagian pertama yang membingungkan, +(+k--)!=0kondisinya. Ini terlihat aneh, karena ini aneh. Tidak seorang pun akan pernah menulis kode seperti itu dalam program nyata. Jadi tidak ada alasan untuk belajar bagaimana memahaminya. (Ya, itu benar, menjelajahi batas-batas suatu sistem dapat membantu Anda belajar tentang poin-poin baiknya, tetapi ada garis yang cukup jelas dalam buku saya antara eksplorasi imajinatif, yang membangkitkan pikiran versus eksplorasi yang dangkal, eksplorasi yang kasar, dan ungkapan ini sangat jelas pada sisi yang salah dari garis itu.)

Bagaimanapun, mari kita periksa +(+k--)!=0. (Dan setelah melakukannya, mari kita lupakan semuanya.) Ekspresi seperti ini harus dipahami dari dalam ke luar. Saya kira Anda tahu apa

k--

tidak. Dibutuhkan knilai saat ini dan "mengembalikan" ke seluruh ekspresi, dan kurang lebih secara bersamaan menurun k, yaitu, ia menyimpan kuantitas k-1kembali k.

Tapi lalu apa fungsinya +? Ini plus unary , bukan plus biner. Ini seperti minus unary. Anda tahu bahwa minus biner tidak mengurangi: ekspresi

a - b

kurangi b dari a. Dan Anda tahu bahwa minus unary meniadakan hal-hal: ekspresi

-a

memberi Anda negatif a. Yang +dilakukan unary adalah ... pada dasarnya tidak ada. +amemberi Anda anilai, setelah mengubah nilai positif menjadi positif dan negatif menjadi negatif. Begitu ekspresinya

+k--

memberi Anda apa pun yang k--memberi Anda, itu adalah knilai lama.

Tapi kita belum selesai, karena sudah

+(+k--)

Ini hanya mengambil apa pun yang +k--memberi Anda, dan berlaku unary +untuk itu lagi. Jadi itu memberi Anda apa pun yang +k--memberi Anda, apa pun yang k--memberi Anda, yang merupakan knilai lama.

Jadi pada akhirnya, kondisinya

while(+(+k--)!=0)

melakukan hal yang persis sama dengan kondisi yang jauh lebih biasa

while(k-- != 0)

akan dilakukan. (Itu juga melakukan hal yang sama dengan kondisi yang terlihat lebih rumit while(+(+(+(+k--)))!=0). Dan kurung itu tidak benar-benar diperlukan; itu juga melakukan hal yang sama seperti yang while(+ + + +k--!=0)seharusnya dilakukan.)

Bahkan mencari tahu apa kondisi "normal"

while(k-- != 0)

Apakah agak rumit. Ada dua hal yang terjadi dalam loop ini: Karena loop berpotensi berulang kali, kita akan:

  1. terus lakukan k--, untuk membuat klebih kecil dan lebih kecil, tetapi juga
  2. tetap melakukan body of the loop, apa pun yang dilakukannya.

Tapi kami melakukan k--bagian itu segera, sebelum (atau dalam proses) memutuskan apakah akan melakukan perjalanan lain melalui loop. Dan ingat bahwa k--"mengembalikan" nilai lama k, sebelum menurunkannya. Dalam program ini, nilai awal kadalah 0. Jadi k--akan "mengembalikan" nilai lama 0, lalu memperbarui kke -1. Tapi kemudian kondisi lainnya adalah != 0- tetapi seperti yang baru saja kita lihat, pertama kali kita menguji kondisi, kita mendapat nilai 0. Jadi kita tidak akan melakukan perjalanan melalui loop, jadi kita tidak akan mencoba untuk mengeksekusi pernyataan bermasalah k=k++sama sekali.

Dengan kata lain, dalam loop khusus ini, meskipun saya mengatakan bahwa "ada dua hal yang terjadi", ternyata hal 1 terjadi satu kali, tetapi hal 2 terjadi nol kali.

Bagaimanapun, saya harap sekarang cukup jelas mengapa alasan yang buruk untuk program ini akhirnya mencetak -1 sebagai nilai akhir dari k. Biasanya, saya tidak suka menjawab pertanyaan kuis seperti ini - rasanya seperti curang - tetapi dalam kasus ini, karena saya sangat tidak setuju dengan seluruh poin latihan, saya tidak keberatan.

Steve Summit
sumber
1
Contoh-contoh yang dibuat khas untuk setiap kelas dasar. Bagaimana lagi orang akan menulis pertanyaan singkat tentang prioritas operator dalam ujian? Saya mengajar matematika, dan jika saya memiliki nikel untuk setiap kali seorang siswa bertanya "Kapan saya akan melakukan ini dalam kehidupan nyata ?" ...
Scott
4
@Scott Ada yang dibuat, dan kemudian ada yang dibuat . Misalkan Anda seorang instruktur pelatihan pengemudi. Misalkan Anda meminta siswa Anda untuk melewati lalu lintas pusat kota yang padat sambil memukulinya mengenai kepala dengan tongkat baseball. Diperdebatkan, siswa akan belajar keterampilan yang berpotensi bermanfaat. Tapi saya menyebut penyalahgunaan ini. Dan saya berdiri pada pendapat saya bahwa kode dalam pertanyaan ini kasar, dengan nilai pedagogis negatif.
Steve Summit
1
"Saya mendukung pendapat saya" ini adil, tetapi kata kunci di sini adalah "pendapat". Jawaban StackOverflow mungkin bukan tempat yang optimal untuk mengekspresikan pendapat seseorang. :)
Scott
1
Sebagai catatan, saya telah mengedit jawaban ini cukup signifikan sejak diterima dan dipilih. Namun pada dasarnya masih mengatakan hal yang sama.
Steve Summit
11

Pada pandangan pertama sepertinya kode ini memanggil perilaku tidak terdefinisi namun itu tidak terjadi.

Pertama mari kita memformat kode dengan benar:

#include<stdio.h>

int main(){
    int k = 0;
    while(+(+k--)!=0)
        k=k++;
    printf("%d\n", k);  
    return 0;
}

Jadi sekarang kita bisa melihat bahwa pernyataan itu k=k++;ada di dalam loop.

Sekarang mari kita telusuri programnya:

Ketika kondisi loop pertama dievaluasi, kmemiliki nilai 0. Ekspresi k--memiliki saat nilai k, yang adalah 0, dan kdecremented sebagai efek samping. Jadi setelah pernyataan ini nilainya k-1.

Garis depan +pada ungkapan ini tidak berpengaruh pada nilai, jadi +k--dievaluasi ke 0 dan juga+(+k--) dievaluasi ke 0.

Kemudian !=operator dievaluasi. Karena 0!=0false, badan loop tidak dimasukkan . Seandainya tubuh dimasukkan, Anda akan memanggil perilaku yang tidak terdefinisi karena k=k++keduanya membaca dan menulis ktanpa titik urutan. Tetapi loop tidak dimasukkan, jadi tidak ada UB.

Akhirnya nilai kdicetak yaitu -1.

dbush
sumber
1
Ini pertanyaan terbuka, eksistensial, filosofis, dan tidak dapat ditanyakan apakah perilaku tidak terdefinisi yang tidak dieksekusi tidak terdefinisi. Apakah itu tidak terdefinisi? Saya kira tidak.
Steve Summit
2
@SteveSummit Sama sekali tidak ada yang eksistensial, filosofis, tidak dapat dibantah tentang hal itu sama sekali. if (x != NULL) *x = 42;Apakah ini tidak ditentukan kapan x == NULL? Tentu saja tidak. Perilaku tidak terdefinisi tidak terjadi di bagian kode yang tidak dieksekusi. Kata perilaku adalah sebuah petunjuk. Kode yang tidak dieksekusi tidak memiliki perilaku, tidak terdefinisi atau sebaliknya.
n. 'kata ganti' m.
1
@SteveSummit Jika perilaku tidak terdefinisi yang tidak dieksekusi akan membatalkan seluruh program, tidak ada program C yang akan mendefinisikan perilaku. Karena program C selalu hanya loop / jika kondisinya jauh dari mengeksekusi UB. Ambil contoh iterasi array sederhana: iterate sampai cek terikat gagal. Menjalankan iterasi loop berikutnya akan menjadi perilaku yang tidak terdefinisi, tetapi karena itu tidak pernah dieksekusi, semuanya baik-baik saja.
cmaster - mengembalikan monica
3
Saya tidak akan berdebat tentang hal itu, karena saya tidak melakukan pengacara bahasa. Tetapi saya bertaruh jika Anda mengajukan pertanyaan ini dan memberi tag-nya pengacara bahasa, Anda bisa melanjutkan perdebatan. Perhatikan bahwa k=k++secara kualitatif berbeda dari *x=42. Yang terakhir didefinisikan dengan baik jika xadalah pointer yang valid, tetapi yang pertama tidak terdefinisi apa pun yang terjadi. (Saya akui Anda mungkin benar, tetapi sekali lagi, saya tidak akan berdebat tentang hal itu, dan saya semakin khawatir kami telah dikuasai secara ahli.)
Steve Summit
1
"Yang terakhir didefinisikan dengan baik jika x adalah penunjuk yang valid, tetapi yang pertama tidak didefinisikan apa pun yang terjadi". Hanya seluruh program yang memiliki perilaku tidak terdefinisi. Gagasan ini tidak berlaku untuk fragmen kode yang diambil secara terpisah.
n. 'kata ganti' m.
3

Berikut ini adalah versi yang menunjukkan prioritas operator:

+(+(k--))

Dua +operator unary tidak melakukan apa-apa, jadi ungkapan ini persis sama dengan k--. Orang yang menulis ini kemungkinan besar mencoba mengacaukan pikiran Anda.

SS Anne
sumber