Mengapa kami memiliki kenaikan postfix?

55

Penafian : Saya tahu benar semantik dari awalan dan penambahan postfix. Jadi tolong jangan jelaskan kepada saya bagaimana mereka bekerja.

Membaca pertanyaan tentang stack overflow, saya tidak bisa tidak melihat bahwa programmer menjadi bingung oleh operator kenaikan postfix berulang-ulang. Dari sini muncul pertanyaan berikut: apakah ada kasus penggunaan di mana peningkatan postfix memberikan manfaat nyata dalam hal kualitas kode?

Izinkan saya menjelaskan pertanyaan saya dengan sebuah contoh. Berikut ini adalah implementasi super ringkas strcpy:

while (*dst++ = *src++);

Tapi itu bukan kode yang paling mendokumentasikan diri sendiri di buku saya (dan menghasilkan dua peringatan yang mengganggu pada kompiler waras). Jadi apa yang salah dengan alternatif berikut?

while (*dst = *src)
{
    ++src;
    ++dst;
}

Kami kemudian dapat menyingkirkan penugasan yang membingungkan dalam kondisi tersebut dan mendapatkan kode yang sepenuhnya bebas peringatan:

while (*src != '\0')
{
    *dst = *src;
    ++src;
    ++dst;
}
*dst = '\0';

(Ya saya tahu, srcdan dstakan memiliki nilai akhir yang berbeda dalam solusi alternatif ini, tetapi karena strcpysegera kembali setelah loop, tidak masalah dalam hal ini.)

Tampaknya tujuan penambahan postfix adalah membuat kode sesingkat mungkin. Saya gagal melihat bagaimana ini adalah sesuatu yang harus kita perjuangkan. Jika ini awalnya tentang kinerja, apakah masih relevan hari ini?

fredoverflow
sumber
7
Varian postfix adalah yang paling umum digunakan, jadi jika Anda akan membuat kasus yang menentangnya, lebih baik menjadi kasus yang cukup kuat. Dan contoh pertama Anda adalah orang yang sangat bodoh, karena saya curiga beberapa orang benar-benar akan mengkode strcpymetode ini (karena alasan yang telah Anda sebutkan).
Robert Harvey
4
Mengapa memiliki keduanya?
James McNellis
31
Jika kami menghapus semuanya dari bahasa yang berpotensi membingungkan programmer, kami tidak akan memiliki banyak fitur. Fakta bahwa sesuatu tidak bermanfaat bagi Anda, atau hanya sangat jarang berguna, tidak berarti harus dihapus. Jika tidak relevan lagi, jangan gunakan; tamat.
Cody Gray
4
Ya tapi Anda tidak pernah bisa melakukannyaint c = 0; c++;
Biff MaGriff
4
@Cody: Karena beberapa hal membingungkan dan bermanfaat. Dia tidak bingung dengan post-increment itu sendiri, dia bingung tentang kegunaannya . Kita seharusnya tidak memiliki hal-hal yang tidak berguna, membingungkan atau tidak.
GManNickG

Jawaban:

23

Meskipun pernah memiliki beberapa implikasi kinerja, saya pikir alasan sebenarnya adalah untuk mengekspresikan niat Anda dengan bersih. Pertanyaan sebenarnya adalah apakah sesuatu while (*d++=*s++);mengekspresikan niat dengan jelas atau tidak. IMO, ya, dan saya menemukan alternatif yang Anda tawarkan kurang jelas - tapi itu mungkin (dengan mudah) akibat menghabiskan waktu puluhan tahun menjadi terbiasa dengan bagaimana hal-hal dilakukan. Setelah belajar C dari K & R (karena ada yang buku hampir tidak ada lain pada C pada saat itu) mungkin membantu juga.

Sampai taraf tertentu, memang benar bahwa keleness dihargai pada tingkat yang jauh lebih besar dalam kode lama. Secara pribadi, saya pikir ini adalah sebagian besar hal yang baik - memahami beberapa baris kode biasanya cukup sepele; Yang sulit adalah memahami potongan kode yang besar. Tes dan penelitian telah menunjukkan berulang kali, bahwa pemasangan semua kode di layar sekaligus adalah faktor utama dalam memahami kode. Ketika layar berkembang, ini tampaknya tetap benar, sehingga menjaga kode (yang wajar) tetap berharga.

Tentu saja mungkin berlebihan, tapi saya rasa ini tidak mungkin. Secara khusus, saya pikir itu berlebihan ketika memahami satu baris kode menjadi sangat sulit atau memakan waktu - khususnya, ketika memahami lebih sedikit baris kode mengkonsumsi lebih banyak upaya daripada memahami lebih banyak baris. Itu sering terjadi pada Lisp dan APL, tetapi tampaknya (setidaknya bagi saya) tidak menjadi masalah di sini.

Saya kurang peduli tentang peringatan kompiler - ini adalah pengalaman saya bahwa banyak kompiler memancarkan peringatan yang sangat konyol secara teratur. Walaupun saya tentu berpikir orang harus memahami kode mereka (dan peringatan apa pun yang mungkin dihasilkannya), kode yang layak yang memicu peringatan di beberapa kompiler tidak selalu salah. Diakui, pemula tidak selalu tahu apa yang dapat mereka abaikan dengan aman, tapi kami tidak tetap pemula selamanya, dan tidak perlu kode seperti kami juga.

Jerry Coffin
sumber
12
+1 untuk menunjukkan bahwa versi singkat lebih mudah dibaca oleh sebagian dari kita. :-)
1
Jika Anda tidak menyukai beberapa peringatan kompiler Anda (dan ada lebih dari beberapa yang dapat saya lakukan tanpa - serius, saya bermaksud mengetik if(x = y), saya bersumpah!) Anda harus menyesuaikan opsi peringatan kompiler Anda sehingga Anda tidak sengaja melewatkan peringatan Anda lakukan seperti.
4
@Brooks Musa: aku banyak kurang diminati tentang itu. Saya tidak suka mencemari kode saya untuk menebus kekurangan kompiler.
Jerry Coffin
1
@ Jerry, @Chris: Anotasi ini dimaksudkan untuk kasus di mana Anda pikir peringatan itu biasanya hal yang baik , tetapi Anda tidak ingin itu berlaku untuk baris tertentu yang melakukan sesuatu yang tidak biasa. Jika Anda tidak pernah menginginkan peringatan dan menganggapnya sebagai kekurangan, gunakan -Wno-X, tetapi jika Anda hanya ingin membuat satu pengecualian untuk itu dan ingin peringatan itu berlaku di tempat lain, ini jauh lebih mudah dipahami daripada menggunakan lompat-lompat misterius seperti Chris merujuk pada .
1
@ Brooks: tanda kurung ganda dan (void)xuntuk membungkam peringatan yang tidak digunakan adalah ungkapan yang cukup terkenal untuk membungkam peringatan yang berguna. Penjelasannya agak mirip pragmas, tidak portabel di terbaik: /
Matthieu M.
32

Ini, err, adalah, hal perangkat keras


Menarik yang akan Anda perhatikan. Peningkatan postfix mungkin ada untuk sejumlah alasan yang sangat bagus, tetapi seperti banyak hal dalam C, popularitasnya dapat ditelusuri ke asal mula bahasa.

Meskipun C dikembangkan pada berbagai mesin awal dan kurang bertenaga, C dan Unix pertama kali mencapai waktu relatif besar dengan model yang dikelola memori dari PDP-11. Ini adalah komputer yang relatif penting pada zaman mereka dan Unix jauh lebih baik - secara eksponensial lebih baik - daripada 7 sistem operasi payah lainnya yang tersedia untuk -11.

Dan, kebetulan, bahwa pada PDP-11,

*--p

dan

*p++

... diimplementasikan dalam perangkat keras sebagai mode pengalamatan. (Juga *p, tetapi tidak ada kombinasi lain.) Pada mesin-mesin awal, semua kurang dari 0,001 GHz, menyimpan satu atau dua instruksi dalam satu lingkaran harus hampir menjadi satu detik menunggu atau satu menit atau keluar untuk Perbedaan -lunch. Ini tidak tepat berbicara dengan postincrement khusus, tetapi loop dengan postincrement pointer bisa jauh lebih baik daripada mengindeks saat itu.

Akibatnya, desain pola karena idiom C yang menjadi makro mental C.

Ini seperti mendeklarasikan variabel setelah {... bukan karena C89 saat ini telah menjadi persyaratan, tetapi sekarang menjadi pola kode.

Pembaruan: Jelas, alasan utama *p++dalam bahasa ini adalah karena itulah yang sering ingin dilakukan seseorang. Popularitas pola kode diperkuat oleh perangkat keras populer yang datang dan cocok dengan pola bahasa yang sudah ada yang dirancang sedikit sebelum kedatangan PDP-11.

Saat ini tidak ada bedanya pola mana yang Anda gunakan, atau jika Anda menggunakan pengindeksan, dan kami biasanya memprogram pada tingkat yang lebih tinggi, tetapi itu pasti sangat berarti pada mesin 0.001GHz, dan menggunakan apa pun selain *--xatau *x++akan berarti Anda tidak "mendapatkan" PDP-11, dan Anda mungkin akan meminta orang-orang mendatangi Anda dan mengatakan "apakah Anda tahu itu ..." :-) :-)

DigitalRoss
sumber
25
Wikipedia mengatakan: "Sebuah mitos rakyat (salah) adalah bahwa arsitektur kumpulan instruksi dari PDP-11 memengaruhi penggunaan bahasa pemrograman C. Penambahan dan penurunan alamat PDP-11 secara otomatis sesuai dengan −−idan i++dikonstruksi dalam C. Jika idan jkeduanya adalah variabel register, ekspresi seperti *(−−i) = *(j++)dapat dikompilasi ke instruksi mesin tunggal. [...] Dennis Ritchie jelas bertentangan dengan mitos rakyat ini. [Perkembangan Bahasa C ] "
fredoverflow
3
Huh (Dari tautan yang disediakan di komentar FredOverflow): "Orang sering menebak bahwa mereka diciptakan untuk menggunakan mode penambahan otomatis dan penurunan alamat otomatis yang disediakan oleh DEC PDP-11 tempat C dan Unix pertama kali menjadi populer. Ini secara historis mustahil, karena tidak ada PDP-11 ketika B dikembangkan .PDP-7, bagaimanapun, memang memiliki beberapa sel memori `auto-increment ', dengan properti yang referensi memori tidak langsung melalui mereka menambah sel. Fitur ini mungkin menyarankan operator tersebut ke Thompson; generalisasi untuk menjadikan keduanya awalan dan postfix adalah miliknya. "
3
DMR hanya mengatakan bahwa PDP-11 bukan alasan itu ditambahkan ke C. Saya mengatakan itu juga. Apa yang saya katakan adalah bahwa itu digunakan untuk keuntungan pada PDP-11 dan menjadi pola desain yang populer karena alasan yang tepat. Jika Wikipedia bertentangan dengan itu maka mereka salah, tetapi saya tidak membacanya seperti itu. Diucapkan dengan buruk di halaman W.
DigitalRoss
3
@FredOverflow. Saya pikir Anda salah paham apa itu "mitos rakyat". Mitosnya adalah C dirancang untuk mengeksploitasi 11 ops itu, bukan karena mereka tidak ada dan tidak bahwa pola kode tidak menjadi populer karena semua orang tahu mereka memetakan ke 11 sumur. Dalam hal itu tidak jelas: PDP-11 benar-benar benar-benar mengimplementasikan kedua mode dalam perangkat keras untuk hampir setiap instruksi sebagai mode pengalamatan, dan itu benar-benar adalah mesin penting pertama yang dijalankan oleh C.
DigitalRoss
2
"Itu pasti sangat berarti pada mesin 0.001GHz". Um, saya benci mengatakannya karena ini sudah berkencan dengan saya, tetapi saya memiliki manual Motorola dan Intel untuk CPU saya untuk mencari tahu ukuran instruksi dan berapa banyak siklus yang mereka ambil. Menemukan kode terkecil ditambahkan untuk dapat menambahkan satu fitur lagi ke spooler cetak dan menyimpan beberapa siklus berarti port serial dapat mengikuti beberapa protokol seperti MIDI yang baru ditemukan. C meringankan beberapa pengambilan nit, terutama ketika kompiler meningkat, tetapi tetap penting mengetahui apa cara paling efisien untuk menulis kode.
the Tin Man
14

Awalan dan postfix --dan ++operator diperkenalkan dalam bahasa B (pendahulu C) oleh Ken Thompson - dan tidak, mereka tidak terinspirasi oleh PDP-11, yang tidak ada pada saat itu.

Mengutip dari "Pengembangan Bahasa C" oleh Dennis Ritchie :

Thompson melangkah lebih jauh dengan menciptakan ++dan--operator, yang kenaikan atau penurunannya; awalan atau posisi postfix menentukan apakah perubahan terjadi sebelum atau setelah mencatat nilai operan. Mereka tidak dalam versi B paling awal, tetapi muncul di sepanjang jalan. Orang-orang sering menebak bahwa mereka diciptakan untuk menggunakan mode penambahan-otomatis dan pengurangan-otomatis yang disediakan oleh DEC PDP-11 tempat C dan Unix pertama kali menjadi populer. Ini secara historis tidak mungkin, karena tidak ada PDP-11 ketika B dikembangkan. Namun, PDP-7 memang memiliki beberapa sel memori 'auto-increment', dengan properti yang referensi memori tidak langsung melalui mereka menambah sel. Fitur ini mungkin menyarankan operator tersebut ke Thompson; generalisasi untuk membuat keduanya awalan dan postfix adalah miliknya Memang,++xlebih kecil dari itu x=x+1.

Keith Thompson
sumber
8
Tidak, saya tidak berhubungan dengan Ken Thompson.
Keith Thompson
Terima kasih untuk referensi yang sangat menarik ini! Ini menegaskan kutipan saya dari K&R asli yang menyarankan tujuan kekompakan daripada optimasi teknis. Namun saya tidak tahu bahwa operator ini sudah ada di B.
Christophe
@BenPen Sejauh yang saya tahu tidak ada kebijakan untuk menutup pertanyaan jika ada duplikat di situs SE yang berbeda , selama kedua pertanyaan sesuai dengan situs masing-masing.
Ordous
Menarik ... Yang Programmer tidak sedekat pertanyaan.
BenPen
@BenPen: Jawaban yang diterima untuk pertanyaan Stack Overflow sudah mengutip sumber yang sama yang saya lakukan (walaupun tidak sebanyak itu).
Keith Thompson
6

Alasan yang jelas untuk operator postincrement ada adalah agar Anda tidak perlu menulis ekspresi seperti (++x,x-1)atau di (x+=1,x-1)semua tempat atau memisahkan pernyataan sepele yang tidak berguna dengan efek samping yang mudah dipahami menjadi beberapa pernyataan. Berikut ini beberapa contohnya:

while (*s) x+=*s++-'0';

if (x) *s++='.';

Setiap kali membaca atau menulis string ke arah depan, postincrement, dan bukan preincrement, hampir selalu merupakan operasi alami. Saya hampir tidak pernah menemukan penggunaan real-dunia untuk preincrement, dan satu-satunya kegunaan utama yang saya temukan untuk predecrement adalah mengubah angka menjadi string (karena sistem penulisan manusia menulis angka mundur).

Sunting: Sebenarnya itu tidak akan begitu jelek; bukannya (++x,x-1)Anda tentu saja dapat menggunakan ++x-1atau (x+=1)-1. Masih x++lebih mudah dibaca.

R ..
sumber
Anda harus berhati-hati tentang ekspresi tersebut karena Anda memodifikasi dan membaca variabel antara dua titik urutan. Urutan di mana ekspresi tersebut dievaluasi tidak dijamin.
@Snowman: Yang mana? Saya tidak melihat masalah seperti itu dalam jawaban saya.
R ..
jika Anda memiliki sesuatu seperti (++x,x-1)(panggilan fungsi, mungkin?)
@Snowman: Itu bukan panggilan fungsi. Ini adalah operator koma C, yang merupakan titik urutan, yang disisipkan untuk mengontrol prioritas. Hasilnya sama dengan x++dalam kebanyakan kasus (satu pengecualian: xadalah tipe yang tidak ditandatangani dengan peringkat lebih rendah dari intdan nilai awal xadalah nilai maksimum dari jenis itu).
R ..
Ah, operator koma yang rumit ... ketika koma bukan koma. Tanda kurung dan kelangkaan operator koma mengusir saya. Lupakan!
4

PDP-11 menawarkan operasi setelah kenaikan dan pra-pengurangan dalam set instruksi. Sekarang ini bukan instruksi. Mereka adalah pengubah instruksi yang memungkinkan Anda menentukan bahwa Anda menginginkan nilai register sebelum ditambahkan atau setelah dikurangi.

Berikut ini adalah langkah penyalinan kata dalam bahasa mesin:

movw (r1)++,(r2)++

yang memindahkan kata dari satu lokasi ke lokasi lain, ditunjuk oleh register, dan register akan siap untuk melakukannya lagi.

Karena C dibangun di atas dan untuk PDP-11, banyak konsep berguna yang masuk ke dalam C. C dimaksudkan sebagai pengganti yang berguna untuk bahasa assembler. Pre-increment dan post-decrement ditambahkan untuk simetri.

BobDalgleish
sumber
Itu pengubah brilian ... Itu membuat saya ingin belajar perakitan PDP-11. BTW, apakah Anda memiliki referensi tentang "untuk simetri?" Masuk akal, tapi ini sejarah, kadang masuk akal bukan kuncinya. LOL
BenPen
2
Juga dikenal sebagai mode pengalamatan . PDP-11 memberikan peningkatan dan pra-penambahan setelah dipasangkan dengan baik untuk operasi stack.
Erik Eidt
1
PDP-8 memberikan tipuan memori pasca-kenaikan, tetapi tidak ada operasi pra-pengurangan yang sesuai. Dengan memori inti magnetik, membaca kata memori menghapusnya (!), Sehingga prosesor menggunakan fungsi tulis-kembali untuk mengembalikan kata yang baru saja dibaca. Menerapkan kenaikan sebelum menulis kembali terjadi secara alami, dan langkah blok yang lebih efisien menjadi mungkin.
Erik Eidt
3
Maaf, PDP-11 tidak menginspirasi operator ini. Itu belum ada ketika Ken Thompson menemukan mereka. Lihat jawaban saya .
Keith Thompson
3

Saya ragu itu benar-benar perlu. Sejauh yang saya tahu, itu tidak dikompilasi menjadi sesuatu yang lebih kompak pada kebanyakan platform daripada menggunakan pra-kenaikan seperti yang Anda lakukan, dalam loop. Hanya saja pada saat itu dibuat, kesederhanaan kode lebih penting daripada kejelasan kode.

Itu bukan untuk mengatakan bahwa kejelasan kode tidak (atau tidak) penting, tetapi ketika Anda mengetik pada modem baud rendah (apa pun di mana Anda mengukur dalam baud lambat) di mana setiap keystroke harus membuatnya menjadi mainframe lalu digemakan kembali satu byte pada satu waktu dengan bit tunggal yang digunakan sebagai parity check, Anda tidak ingin harus mengetik banyak.

Ini seperti & & | operator memiliki prioritas lebih rendah dari ==

Itu dirancang dengan niat terbaik (versi && dan || non-korsleting), tapi sekarang membingungkan programmer setiap hari, dan mungkin tidak akan pernah berubah.

Bagaimanapun, ini hanya sebuah jawaban karena saya pikir tidak ada jawaban yang baik untuk pertanyaan Anda, tapi saya mungkin akan terbukti salah oleh guru yang lebih menguasai daripada saya.

--SUNTING--

Saya harus mencatat bahwa saya menemukan keduanya sangat berguna, saya hanya menunjukkan bahwa itu tidak akan berubah apakah ada yang suka atau tidak.


sumber
Ini bahkan tidak benar. Tidak ada titik "kesederhanaan kode" lebih penting daripada kejelasan.
Cody Gray
Yah, saya berpendapat bahwa itu lebih dari titik fokus. Anda cukup benar, tentu saja tidak pernah lebih penting daripada kejelasan kode ... bagi kebanyakan orang.
6
Nah, definisi "singkat" adalah "lancar elegan: dipoles" atau "menggunakan beberapa kata: tanpa superfluity," yang keduanya umumnya merupakan hal yang baik ketika menulis kode. "Terse" tidak berarti "tidak jelas, sulit dibaca, dan dikaburkan" seperti yang dipikirkan banyak orang.
James McNellis
@ James: Walaupun Anda benar, saya pikir kebanyakan orang mengartikan definisi kedua "singkat" ketika menggunakannya dalam konteks pemrograman: "menggunakan beberapa kata; tanpa superfluitas". Di bawah definisi itu, tentu lebih mudah untuk membayangkan situasi di mana ada trade-off antara singkat dan ekspresifnya sepotong kode tertentu. Tentunya hal yang benar untuk dilakukan ketika menulis kode adalah hal yang sama seperti ketika berbicara: membuat hal-hal sesingkat yang seharusnya, tetapi tidak lebih pendek. Menilai kesederhanaan atas ekspresivitas dapat menyebabkan kode yang tampak membingungkan, tetapi jelas tidak seharusnya.
Cody Gray
1
mundur 40 tahun dan bayangkan Anda harus menyesuaikan program Anda pada drum yang hanya dapat menampung sekitar 1000 karakter, atau menulis kompiler hanya dapat bekerja dengan 4 ribu byte ram. Ada saat di mana kesederhanaan vs kejelasan bahkan tidak menjadi masalah. Anda harus singkat, tidak ada komputer di planet ini yang dapat menyimpan, menjalankan atau mengkompilasi program non-terse Anda.
no.
3

Saya suka aperator postfix ketika berhadapan dengan pointer (apakah saya atau tidak dereferencing mereka) karena

p++

dibaca lebih alami sebagai "pindah ke tempat berikutnya" daripada yang setara

p += 1

yang pasti harus membingungkan pemula saat pertama kali mereka melihatnya digunakan dengan pointer di mana sizeof (* p)! = 1.

Apakah Anda yakin menyatakan masalah kebingungan dengan benar? Bukan hal yang membingungkan pemula fakta bahwa operator postfix ++ memiliki prioritas lebih tinggi daripada operator dereference * sehingga

*p++

mem-parsing sebagai

*(p++)

dan tidak

(*p)++

karena beberapa mungkin mengharapkan?

(Anda pikir itu buruk? Lihat Membaca Deklarasi C. )

Eric Giguere
sumber
2

Di antara elemen halus dari pemrograman yang baik adalah lokalisasi dan minimalis :

  • menempatkan variabel dalam ruang lingkup penggunaan minimal
  • menggunakan const ketika akses tulis tidak diperlukan
  • dll.

Dengan semangat yang sama, x++dapat dilihat sebagai cara melokalkan referensi ke nilai saat ini xsambil segera menunjukkan bahwa Anda - setidaknya untuk saat ini - selesai dengan nilai itu dan ingin pindah ke yang berikutnya (valid apakah xada intatau sebuah pointer atau iterator). Dengan sedikit imajinasi, Anda dapat membandingkan ini dengan membiarkan nilai lama / posisi xkeluar "dari ruang lingkup" segera setelah itu tidak lagi diperlukan, dan pindah ke nilai baru. Titik tepat di mana transisi itu dimungkinkan disorot oleh postfix++ . Implikasi bahwa ini mungkin adalah penggunaan akhir dari "lama" xdapat menjadi wawasan yang berharga ke dalam algoritma, membantu programmer saat mereka memindai melalui kode di sekitarnya. Tentu saja, postfix++ dapat membuat programmer mencari penggunaan nilai baru, yang mungkin atau mungkin tidak baik tergantung pada kapan nilai baru itu benar-benar dibutuhkan, jadi itu merupakan aspek dari "seni" atau "kerajinan" pemrograman untuk menentukan apa yang lebih membantu dalam keadaan.

Sementara banyak pemula mungkin bingung dengan fitur, ada baiknya menyeimbangkannya dengan manfaat jangka panjang: pemula tidak tinggal lama untuk pemula.

Tony
sumber
2

Wow, banyak jawaban yang tidak tepat (jika saya berani), dan tolong maafkan saya jika saya menunjukkan yang jelas - terutama mengingat komentar Anda untuk tidak menunjukkan semantik, tetapi jelas (dari sudut pandang Stroustrup, saya kira) tampaknya belum diposting! :)

Postfix x++menghasilkan temporer yang dilewati 'ke atas' dalam ekspresi, sementara variabel xselanjutnya bertambah.

Awalan ++xtidak menghasilkan objek sementara, tetapi menambah 'x' dan meneruskan hasilnya ke ekspresi.

Nilainya adalah kenyamanan, bagi mereka yang mengenal operator.

Sebagai contoh:

int x = 1;
foo(x++); // == foo(1)
// x == 2: true

x = 1;
foo(++x); // == foo(2)
// x == 2: true

Tentu saja, hasil dari contoh ini dapat dihasilkan dari kode lain yang setara (dan mungkin kurang miring).

Jadi mengapa kita memiliki operator postfix? Saya kira karena itu adalah ungkapan yang tetap ada, terlepas dari kebingungan yang ditimbulkannya. Ini adalah konstruksi peninggalan yang dulu dianggap memiliki nilai, meskipun saya tidak yakin bahwa nilai yang dirasakan begitu banyak untuk kinerja dan untuk kenyamanan. Kenyamanan itu belum hilang, tetapi saya pikir apresiasi keterbacaan telah meningkat, sehingga mempertanyakan tujuan operator.

Apakah kita memerlukan operator postfix lagi? Mungkin tidak. Hasilnya membingungkan dan menciptakan penghalang bagi pemahaman. Beberapa coder yang baik pasti akan segera tahu di mana menemukannya, sering di tempat-tempat di mana ia memiliki keindahan "PERL-esque" yang khas. Biaya keindahan itu adalah keterbacaan.

Saya setuju bahwa kode eksplisit memiliki kelebihan dibandingkan dengan kesederhanaan, dalam keterbacaan, dapat dimengerti dan pemeliharaan. Desainer dan manajer yang baik menginginkan hal itu.

Namun, ada keindahan tertentu yang diakui oleh beberapa programmer yang mendorong mereka untuk menghasilkan kode dengan hal-hal murni untuk kesederhanaan yang indah - seperti operator postfix - kode mana yang tidak pernah mereka pikirkan - atau ditemukan merangsang secara intelektual. Ada sesuatu yang pasti yang membuatnya lebih indah, jika tidak lebih diinginkan, terlepas dari kekesalan.

Dengan kata lain, beberapa orang menemukan while (*dst++ = *src++);solusi sederhana yang lebih indah, sesuatu yang membuat Anda takjub dengan kesederhanaannya, seperti halnya menyikat kanvas. Bahwa Anda dipaksa untuk memahami bahasa untuk menghargai keindahan hanya menambah keindahannya.

Brian M. Hunt
sumber
3
+1 "beberapa orang menemukan sementara (* dst ++ = * src ++); untuk menjadi solusi yang lebih cantik". Pastinya. Orang-orang yang tidak mengerti bahasa assembly tidak benar-benar memahami betapa elegannya C, tetapi pernyataan kode khusus itu adalah bintang yang bersinar.
the Tin Man
"Apakah kita membutuhkan operator postfix lagi? Mungkin tidak." - Saya tidak bisa tidak memikirkan mesin Turing di sini. Pernahkah kita membutuhkan variabel otomatis, forpernyataan, heck, bahkan while(kita punya goto, setelah semua)?
@Bernd: Ha! Bayangkan penutupan! Penutupan! Memalukan! lol
Brian M. Hunt
Penutupan! Sungguh konyol. Beri aku kaset! Serius, apa yang saya maksudkan adalah bahwa saya tidak berpikir / membutuhkan / fitur harus menjadi kriteria keputusan utama (kecuali jika Anda ingin merancang, katakanlah, lisp). Saya menggunakan IME (tidak, perlu ) operator postfix hampir secara eksklusif, dan hanya jarang yang membutuhkan awalan saja.
2

Mari kita lihat pembenaran asli Kernighan & Ritchie (K&R asli halaman 42 dan 43):

Aspek yang tidak biasa adalah bahwa ++ dan - dapat digunakan sebagai awalan atau sebagai postfix. (...) Dalam konteks di mana tidak ada nilai yang diinginkan (..) pilih awalan atau postfix sesuai selera. Tapi htere adalah situasi di mana satu atau yang lain secara khusus dipanggil.

Teks berlanjut dengan beberapa contoh yang menggunakan kenaikan dalam indeks, dengan tujuan eksplisit menulis kode " lebih kompak ". Jadi alasan di balik operator ini adalah kenyamanan kode yang lebih ringkas.

Tiga contoh yang diberikan ( squeeze(), getline()dan strcat()) hanya menggunakan postfix dalam ekspresi menggunakan pengindeksan. Penulis membandingkan kode dengan versi yang lebih panjang yang tidak menggunakan penambahan yang disematkan. Ini menegaskan bahwa fokusnya adalah pada kekompakan.

Sorot K&R pada halaman 102, penggunaan operator ini dalam kombinasi dengan penunjuk titik (misalnya, *--pdan *p--). Tidak ada contoh lebih lanjut yang diberikan, tetapi sekali lagi, mereka menjelaskan bahwa manfaatnya adalah kekompakan.

Awalan misalnya sangat umum digunakan ketika menurunkan indeks atau pointer yang menatap dari ujung.

 int i=0; 
 while (i<10) 
     doit (i++);  // calls function for 0 to 9  
                  // i is 10 at this stage
 while (--i >=0) 
     doit (i);    // calls function from 9 to 0 
Christophe
sumber
1

Saya akan tidak setuju dengan premis bahwa ++p,, p++entah bagaimana sulit dibaca atau tidak jelas. Satu berarti "kenaikan p dan kemudian baca p", yang lain berarti "baca p dan kemudian kenaikan p". Dalam kedua kasus tersebut, operator dalam kode persis berada di tempat di dalam penjelasan kode, jadi jika Anda tahu apa ++artinya, Anda tahu apa artinya kode yang dihasilkan.

Anda dapat membuat kode yang dikaburkan menggunakan sintaksis apa pun , tetapi saya tidak melihat suatu kasus di sini bahwa p++/ ++psecara inheren membingungkan.

philosodad
sumber
1

ini dari POV seorang insinyur listrik:

ada banyak prosesor yang memiliki operator post-increment dan pre-decrement built-in untuk tujuan mempertahankan tumpukan LIFO (last-in-first-out-out).

mungkin seperti:

 float stack[4096];
 int stack_pointer = 0;

 ... 

 #define push_stack(arg) stack[stack_pointer++] = arg;
 #define pop_stack(arg) arg = stack[--stack_pointer];

 ...

Saya tidak tahu, tapi itulah alasan saya akan berharap untuk melihat baik operator kenaikan prefix dan postfix.

robert bristow-johnson
sumber
1

Untuk menggarisbawahi poin Christophe tentang kekompakan, saya ingin menunjukkan kepada Anda semua kode dari V6. Ini adalah bagian dari kompiler C asli, dari http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/source/c/c00.c :

/*
 * Look up the identifier in symbuf in the symbol table.
 * If it hashes to the same spot as a keyword, try the keyword table
 * first.  An initial "." is ignored in the hash.
 * Return is a ptr to the symbol table entry.
 */
lookup()
{
    int ihash;
    register struct hshtab *rp;
    register char *sp, *np;

    ihash = 0;
    sp = symbuf;
    if (*sp=='.')
        sp++;
    while (sp<symbuf+ncps)
        ihash =+ *sp++;
    rp = &hshtab[ihash%hshsiz];
    if (rp->hflag&FKEYW)
        if (findkw())
            return(KEYW);
    while (*(np = rp->name)) {
        for (sp=symbuf; sp<symbuf+ncps;)
            if (*np++ != *sp++)
                goto no;
        csym = rp;
        return(NAME);
    no:
        if (++rp >= &hshtab[hshsiz])
            rp = hshtab;
    }
    if(++hshused >= hshsiz) {
        error("Symbol table overflow");
        exit(1);
    }
    rp->hclass = 0;
    rp->htype = 0;
    rp->hoffset = 0;
    rp->dimp = 0;
    rp->hflag =| xdflg;
    sp = symbuf;
    for (np=rp->name; sp<symbuf+ncps;)
        *np++ = *sp++;
    csym = rp;
    return(NAME);
}

Ini adalah kode yang diedarkan dalam samizdat sebagai pendidikan dengan gaya yang baik, namun kita tidak akan pernah menulisnya seperti ini hari ini. Lihatlah berapa banyak efek samping yang telah dikemas ke dalam ifdan whilekondisi, dan sebaliknya, bagaimana kedua forloop tidak memiliki ekspresi kenaikan karena itu dilakukan dalam tubuh loop. Lihatlah bagaimana balok hanya dibungkus dengan kawat gigi ketika benar-benar diperlukan.

Bagian dari ini tentu saja optimasi mikro manual dari jenis yang kami harapkan akan dilakukan oleh kompiler untuk kami hari ini, tetapi saya juga akan mengemukakan hipotesis bahwa ketika seluruh antarmuka Anda ke komputer adalah tty glass 80x25 tunggal, Anda ingin kode Anda menjadi sepadat mungkin sehingga Anda dapat melihatnya lebih banyak pada saat yang sama.

(Penggunaan buffer global untuk segala sesuatu mungkin merupakan akibat dari memotong gigi seseorang pada bahasa assembly.)

zwol
sumber
Tangani dengan hati-hati: "ihash = + * sp ++;" Pada titik tertentu C tidak hanya + = tetapi juga = + operator. Hari ini, = + adalah operator penugasan, diikuti oleh unary plus yang tidak melakukan apa-apa, jadi itu setara dengan "ihash = * sp; sp + = 1;"
gnasher729
@ gnasher729 Ya, dan kemudian rp->hflag =| xdflg, yang saya percaya akan menjadi kesalahan sintaks pada kompiler modern. "Pada titik tertentu" tepatnya V6, seperti yang terjadi; perubahan untuk +=membentuk happend di V7. (Kompiler V6 hanya diterima =+, jika saya membaca kode ini dengan benar - lihat simbol () dalam file yang sama.)
zwol
-1

Mungkin Anda harus memikirkan kosmetik juga.

while( i++ )

bisa dibilang "terlihat lebih baik" daripada

while( i+=1 )

Untuk beberapa alasan, operator pasca kenaikan terlihat sangat menarik. Pendek, manis, dan meningkatkan segalanya dengan 1. Bandingkan dengan awalan:

while( ++i )

"Terlihat mundur" bukan? Anda tidak pernah melihat tanda tambah PERTAMA dalam matematika, kecuali ketika seseorang bodoh dengan menentukan sesuatu yang positif ( +0atau sesuatu seperti itu). Kami terbiasa melihat OBYEK + SESUATU

Apakah ada hubungannya dengan penilaian? A, A +, A ++? Saya dapat memberi tahu Anda bahwa saya sangat terhina pada awalnya bahwa orang-orang Python dihapus operator++dari bahasa mereka!

bobobobo
sumber
1
Contoh Anda tidak melakukan hal yang sama. i++mengembalikan nilai asli i, dan i+=1dan ++imengembalikan nilai asli iditambah 1. Karena Anda menggunakan kembali nilai-nilai untuk aliran kontrol, hal-hal ini.
David Thornley
Ya kau benar. Tapi saya hanya melihat keterbacaan di sini.
bobobobo
-2

Menghitung mundur 9 hingga 0 inklusif:

for (unsigned int i=10; i-- > 0;)  {
    cout << i << " ";
}

Atau loop sementara yang setara.


sumber
1
Bagaimana tentang for (unsigned i = 9; i <= 9; --i) ?
fredoverflow