Mengapa c = ++ (a + b) memberikan kesalahan kompilasi?

111

Setelah meneliti, saya membaca bahwa operator increment mengharuskan operan memiliki objek data yang dapat dimodifikasi: https://en.wikipedia.org/wiki/Increment_and_decrement_operators .

Dari sini saya kira itu memberikan kesalahan kompilasi karena (a+b) merupakan integer sementara dan tidak dapat dimodifikasi.

Apakah pemahaman ini benar? Ini adalah pertama kalinya saya mencoba meneliti masalah jadi jika ada sesuatu yang seharusnya saya cari, mohon saran.

dng
sumber
35
Itu tidak buruk dalam hal penelitian. Anda berada di jalur yang benar.
StoryTeller - Unslander Monica
35
Apa yang Anda harapkan dari ekspresi tersebut?
qrdl
4
sesuai dengan standar C11 6.5.3.1: Operand operator increment atau decrement awalan harus memiliki tipe atomik, memenuhi syarat, atau tidak memenuhi syarat nyata atau penunjuk, dan harus menjadi nilai l yang dapat dimodifikasi
Christian Gibbons
10
Bagaimana Anda ingin 1 didistribusikan antara a dan b? "Haruskah indeks array dimulai dari 0 atau 1? Kompromi saya sebesar 0,5 ditolak tanpa, menurut saya, pertimbangan yang tepat." - Stan Kelly-Bootle
Andrew Morton
5
Saya pikir pertanyaan lanjutannya adalah mengapa Anda ingin melakukan ini ketika c = a + b + 1membuat niat Anda lebih jelas dan juga lebih pendek untuk diketik. Operator increment / decrement melakukan dua hal 1. mereka dan argumen mereka membentuk ekspresi (yang dapat digunakan, misalnya dalam loop for), 2. mereka memodifikasi argumen. Dalam contoh Anda, Anda menggunakan properti 1. tetapi bukan properti 2., karena Anda membuang argumen yang dimodifikasi. Jika Anda tidak membutuhkan properti 2. dan hanya ingin ekspresi, maka Anda dapat menulis ekspresi, misalnya x + 1, bukan x ++.
Trevor

Jawaban:

117

Itu hanya aturan, itu saja, dan mungkin ada untuk (1) membuatnya lebih mudah untuk menulis compiler C dan (2) tidak ada yang meyakinkan komite standar C untuk melonggarkannya.

Secara informal Anda hanya dapat menulis ++foojika foodapat muncul di sisi kiri ekspresi tugas seperti foo = bar. Karena Anda tidak bisa menulis a + b = bar, Anda juga tidak bisa menulis ++(a + b).

Tidak ada alasan nyata mengapa a + btidak bisa menghasilkan sementara yang ++dapat beroperasi, dan akibatnya adalah nilai ekspresi ++(a + b).

Batsyeba
sumber
4
Saya pikir poin (1) menyentuh paku di kepala. Hanya melihat aturan untuk perwujudan sementara di C ++ dapat mengubah perut seseorang (tapi itu kuat, harus mengatakan itu).
StoryTeller - Unslander Monica
4
@StoryTeller: Memang, tidak seperti bahasa C ++ kami yang tercinta, C masih dikompilasi ke dalam assembly dengan relatif sepele.
Batsyeba
29
Inilah alasan sebenarnya IMHO: akan menjadi kebingungan yang mengerikan jika ++terkadang memiliki efek samping dari memodifikasi sesuatu dan terkadang tidak.
aschepler
5
@dng: Memang benar; itulah sebabnya istilah lvalue dan rvalue diperkenalkan, meskipun sekarang lebih rumit dari itu (terutama di C ++). Misalnya, konstanta tidak pernah bisa menjadi nilai l: sesuatu seperti 5 = a tidak masuk akal.
Batsyeba
6
@Bathsheba Itu menjelaskan mengapa 5 ++ juga menyebabkan kesalahan kompilasi
dng
40

Standar C11 menyatakan di bagian 6.5.3.1

Operand operator increment atau decrement prefiks harus memiliki tipe atomic, qualified, or unqualified real atau pointer, dan harus berupa lvalue

Dan "nilai l yang dapat dimodifikasi" dijelaskan di bagian 6.3.2.1 sub-bagian 1

Nilai l adalah ekspresi (dengan tipe objek selain void) yang berpotensi menunjukkan objek; jika nilai l tidak menunjukkan objek saat dievaluasi, perilaku tidak terdefinisi. Ketika sebuah objek dikatakan memiliki tipe tertentu, tipe ditentukan oleh lvalue yang digunakan untuk menunjuk objek tersebut. Nilai l yang dapat dimodifikasi adalah nilai l yang tidak memiliki tipe larik, tidak memiliki tipe yang tidak lengkap, tidak memiliki jenis yang memenuhi syarat konst, dan jika itu adalah struktur atau gabungan, tidak memiliki anggota (termasuk, secara rekursif, anggota mana pun atau elemen dari semua agregat atau gabungan yang terkandung) dengan tipe yang memenuhi syarat konstanta.

Jadi (a+b)bukan nilai l yang dapat dimodifikasi dan oleh karena itu tidak memenuhi syarat untuk operator kenaikan prefiks.

Christian Gibbons
sumber
1
Kesimpulan Anda dari definisi ini hilang ... Anda ingin mengatakan bahwa (a + b) tidak berpotensi menunjuk suatu objek, tetapi paragraf ini tidak mengizinkannya.
hkBst
21

Anda benar. yang ++mencoba untuk menetapkan nilai baru untuk variabel asli. Jadi ++aakan mengambil nilai a, menambahkannya 1dan kemudian menetapkannya kembali a. Karena, seperti yang Anda katakan, (a + b) adalah nilai temp, dan bukan variabel dengan alamat memori yang ditetapkan, penetapan tidak dapat dilakukan.

Roee Gavirel
sumber
12

Saya pikir Anda sebagian besar menjawab pertanyaan Anda sendiri. Saya mungkin membuat perubahan kecil pada frase Anda dan mengganti "variabel sementara" dengan "rvalue" seperti yang disebutkan C.Gibbons.

Variabel istilah, argumen, variabel sementara, dan sebagainya akan menjadi lebih jelas saat Anda mempelajari model memori C (ini terlihat seperti gambaran umum yang bagus: https://www.geeksforgeeks.org/memory-layout-of-c-program/ ).

Istilah "rvalue" mungkin tampak buram ketika Anda baru memulai, jadi saya harap hal berikut membantu mengembangkan intuisi tentangnya.

Lvalue / rvalue berbicara tentang sisi yang berbeda dari tanda sama dengan (operator penugasan): lvalue = sisi kiri (huruf kecil L, bukan "satu") rvalue = sisi kanan

Mempelajari sedikit tentang bagaimana C menggunakan memori (dan register) akan membantu untuk melihat mengapa perbedaan itu penting. Dalam sapuan kuas yang luas , kompilator membuat daftar instruksi bahasa mesin yang menghitung hasil ekspresi (nilai r) dan kemudian meletakkan hasilnya di suatu tempat (nilai l). Bayangkan seorang kompiler berurusan dengan fragmen kode berikut:

x = y * 3

Dalam perakitan pseudocode mungkin terlihat seperti contoh mainan ini:

load register A with the value at memory address y
load register B with a value of 3
multiply register A and B, saving the result in A
write register A to memory address x

Operator ++ (dan - mitranya) membutuhkan "tempat" untuk memodifikasi, pada dasarnya apa pun yang dapat bekerja sebagai nilai l.

Memahami model memori C akan membantu karena Anda akan mendapatkan ide yang lebih baik di kepala Anda tentang bagaimana argumen diteruskan ke fungsi dan (akhirnya) bagaimana bekerja dengan alokasi memori dinamis, seperti fungsi malloc (). Untuk alasan serupa Anda mungkin mempelajari beberapa pemrograman perakitan sederhana di beberapa titik untuk mendapatkan ide yang lebih baik tentang apa yang dilakukan kompilator. Juga jika Anda menggunakan gcc , opsi -S "Berhenti setelah tahap kompilasi tepat; jangan merakit." bisa menarik (meskipun saya akan merekomendasikan mencobanya pada potongan kode kecil ).

Sekadar tambahan: Instruksi ++ telah ada sejak 1969 (meskipun itu dimulai pada pendahulu C, B):

Pengamatan (Ken Thompson) adalah bahwa terjemahan dari ++ x lebih kecil daripada terjemahan x = x + 1. "

Setelah referensi wikipedia itu akan membawa Anda ke tulisan menarik oleh Dennis Ritchie ("R" dalam "K&R C") tentang sejarah bahasa C, ditautkan di sini untuk kenyamanan: http://www.bell-labs.com/ usr / dmr / www / chist.html tempat Anda dapat mencari "++".

jgreve
sumber
6

Alasannya adalah bahwa standar membutuhkan operan menjadi nilai l. Ekspresi (a+b)tersebut bukan nilai l, jadi menerapkan operator kenaikan tidak diperbolehkan.

Sekarang, orang mungkin mengatakan "Oke, itu memang alasannya, tetapi sebenarnya tidak ada alasan * nyata * selain itu" , tetapi sayangnya kata-kata tertentu tentang cara kerja operator secara faktual memang mengharuskan demikian.

Ekspresi ++ E setara dengan (E + = 1).

Jelas, Anda tidak dapat menulis E += 1jika Ebukan nilai l. Yang memalukan karena orang bisa saja mengatakan: "menambah E per satu" dan selesai. Dalam hal ini, menerapkan operator pada nilai non-l akan (pada prinsipnya) sangat mungkin, dengan mengorbankan membuat kompilator sedikit lebih kompleks.

Sekarang, definisi tersebut dapat dengan mudah diubah kata-katanya (saya pikir ini bukan aslinya C tetapi merupakan pusaka dari B), tetapi melakukan hal itu secara mendasar akan mengubah bahasa menjadi sesuatu yang tidak lagi kompatibel dengan versi sebelumnya. Karena keuntungan yang mungkin didapat agak kecil tetapi kemungkinan implikasinya sangat besar, itu tidak pernah terjadi dan mungkin tidak akan pernah terjadi.

Jika Anda mempertimbangkan C ++ selain C (pertanyaan diberi tag C, tetapi ada diskusi tentang kelebihan operator), ceritanya menjadi lebih rumit. Dalam C, sulit untuk membayangkan bahwa ini bisa terjadi, tetapi dalam C ++ hasil dari (a+b)bisa jadi sesuatu yang tidak bisa Anda tambahkan sama sekali, atau penambahan bisa memiliki efek samping yang sangat besar (tidak hanya menambahkan 1). Kompilator harus mampu mengatasinya, dan mendiagnosis kasus bermasalah saat terjadi. Pada nilai l, itu masih agak sepele untuk diperiksa. Tidak demikian halnya untuk ekspresi sembarangan apa pun di dalam tanda kurung yang Anda lemparkan pada hal buruk.
Ini bukanlah alasan sebenarnya mengapa tidak bisa dilakukan, tetapi ini pasti memberikan penjelasan mengapa orang-orang yang menerapkan ini tidak terlalu senang menambahkan fitur seperti itu yang menjanjikan sedikit manfaat bagi sangat sedikit orang.

Damon
sumber
3

(a + b) mengevaluasi ke nilai r, yang tidak bisa ditambah.

Casper B. Hansen
sumber
3

++ mencoba memberikan nilai ke variabel asli dan karena (a + b) adalah nilai temp, ia tidak dapat melakukan operasi. Dan mereka pada dasarnya adalah aturan dari konvensi pemrograman C untuk membuat pemrograman menjadi mudah. Itu dia.

Babu Chandermani
sumber
2

Ketika ekspresi ++ (a + b) dilakukan, maka misalnya:

int a, b;
a = 10;
b = 20;
/* NOTE :
 //step 1: expression need to solve first to perform ++ operation over operand
   ++ ( exp );
// in your case 
   ++ ( 10 + 20 );
// step 2: result of that inc by one 
   ++ ( 30 );
// here, you're applying ++ operator over constant value and it's invalid use of ++ operator 
*/
++(a+b);
Jeet Parikh
sumber