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, src
dan dst
akan memiliki nilai akhir yang berbeda dalam solusi alternatif ini, tetapi karena strcpy
segera 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?
strcpy
metode ini (karena alasan yang telah Anda sebutkan).int c = 0; c++;
Jawaban:
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.
sumber
if(x = y)
, saya bersumpah!) Anda harus menyesuaikan opsi peringatan kompiler Anda sehingga Anda tidak sengaja melewatkan peringatan Anda lakukan seperti.-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 .(void)x
untuk membungkam peringatan yang tidak digunakan adalah ungkapan yang cukup terkenal untuk membungkam peringatan yang berguna. Penjelasannya agak mirippragmas
, tidak portabel di terbaik: /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,
dan
... 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
*--x
atau*x++
akan berarti Anda tidak "mendapatkan" PDP-11, dan Anda mungkin akan meminta orang-orang mendatangi Anda dan mengatakan "apakah Anda tahu itu ..." :-) :-)sumber
−−i
dani++
dikonstruksi dalam C. Jikai
danj
keduanya adalah variabel register, ekspresi seperti*(−−i) = *(j++)
dapat dikompilasi ke instruksi mesin tunggal. [...] Dennis Ritchie jelas bertentangan dengan mitos rakyat ini. [Perkembangan Bahasa C ] "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 :
sumber
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: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-1
atau(x+=1)-1
. Masihx++
lebih mudah dibaca.sumber
(++x,x-1)
(panggilan fungsi, mungkin?)x++
dalam kebanyakan kasus (satu pengecualian:x
adalah tipe yang tidak ditandatangani dengan peringkat lebih rendah dariint
dan nilai awalx
adalah nilai maksimum dari jenis itu).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:
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.
sumber
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
Saya suka aperator postfix ketika berhadapan dengan pointer (apakah saya atau tidak dereferencing mereka) karena
dibaca lebih alami sebagai "pindah ke tempat berikutnya" daripada yang setara
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
mem-parsing sebagai
dan tidak
karena beberapa mungkin mengharapkan?
(Anda pikir itu buruk? Lihat Membaca Deklarasi C. )
sumber
Di antara elemen halus dari pemrograman yang baik adalah lokalisasi dan minimalis :
Dengan semangat yang sama,
x++
dapat dilihat sebagai cara melokalkan referensi ke nilai saat inix
sambil segera menunjukkan bahwa Anda - setidaknya untuk saat ini - selesai dengan nilai itu dan ingin pindah ke yang berikutnya (valid apakahx
adaint
atau sebuah pointer atau iterator). Dengan sedikit imajinasi, Anda dapat membandingkan ini dengan membiarkan nilai lama / posisix
keluar "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"x
dapat 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.
sumber
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 variabelx
selanjutnya bertambah.Awalan
++x
tidak menghasilkan objek sementara, tetapi menambah 'x' dan meneruskan hasilnya ke ekspresi.Nilainya adalah kenyamanan, bagi mereka yang mengenal operator.
Sebagai contoh:
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.sumber
for
pernyataan, heck, bahkanwhile
(kita punyagoto
, setelah semua)?Mari kita lihat pembenaran asli Kernighan & Ritchie (K&R asli halaman 42 dan 43):
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()
danstrcat()
) 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,
*--p
dan*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.
sumber
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++
/++p
secara inheren membingungkan.sumber
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:
Saya tidak tahu, tapi itulah alasan saya akan berharap untuk melihat baik operator kenaikan prefix dan postfix.
sumber
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 :
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
if
danwhile
kondisi, dan sebaliknya, bagaimana keduafor
loop 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.)
sumber
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.)Mungkin Anda harus memikirkan kosmetik juga.
bisa dibilang "terlihat lebih baik" daripada
Untuk beberapa alasan, operator pasca kenaikan terlihat sangat menarik. Pendek, manis, dan meningkatkan segalanya dengan 1. Bandingkan dengan awalan:
"Terlihat mundur" bukan? Anda tidak pernah melihat tanda tambah PERTAMA dalam matematika, kecuali ketika seseorang bodoh dengan menentukan sesuatu yang positif (
+0
atau sesuatu seperti itu). Kami terbiasa melihat OBYEK + SESUATUApakah 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!sumber
i++
mengembalikan nilai aslii
, dani+=1
dan++i
mengembalikan nilai aslii
ditambah 1. Karena Anda menggunakan kembali nilai-nilai untuk aliran kontrol, hal-hal ini.Menghitung mundur 9 hingga 0 inklusif:
Atau loop sementara yang setara.
sumber
for (unsigned i = 9; i <= 9; --i)
?