Baru-baru ini saya menemukan masalah yang tidak dapat saya pahami sendiri.
Apa arti ketiga Ekspresi ini BENAR - BENAR ?
*ptr++
*++ptr
++*ptr
Saya sudah mencoba Ritchie. Namun sayangnya tidak dapat mengikuti apa yang dia katakan tentang 3 operasi ini.
Saya tahu mereka semua dilakukan untuk meningkatkan pointer / nilai yang ditunjukkan. Saya juga bisa menebak mungkin ada banyak hal tentang prioritas dan urutan evaluasi. Seperti satu penambahan pointer pertama kemudian mengambil konten pointer itu, satu hanya mengambil konten dan kemudian menambah pointer dll. Seperti yang Anda lihat, saya tidak memiliki pemahaman yang jelas tentang operasi aktual mereka , yang saya ingin jelas sesegera mungkin. Tetapi saya benar-benar bingung ketika saya mendapat kesempatan untuk menerapkannya dalam program. Sebagai contoh:
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
memberi saya hasil ini:
ello
Tapi harapan saya adalah bahwa itu akan dicetak Hello
. Satu permintaan terakhir - Tolong beri saya contoh untuk bagaimana setiap ekspresi bekerja dalam potongan kode yang diberikan. Karena sebagian besar waktu hanya paragraf teori belaka yang terbang di atas kepala saya.
(*ptr)++
(tanda kurung diperlukan untuk disambiguasi dari*ptr++
)char* p
,, sambil menunjuk ke string karakter unik yang dihentikan yang valid. Kemudian memiliki fungsifn(char ch)
yang mencetak kedua yangch
parameter dan char saat ini ditunjuk olehp
. Sekarang panggilfn(*p++);
Q: Apakahfn
mencetak karakter yang sama dua kali ? Anda akan kagum dengan banyaknya profesor yang salah menjawab pertanyaan itu.const char* p = "Hello";
Jawaban:
Inilah penjelasan terperinci yang saya harap akan membantu. Mari kita mulai dengan program Anda, karena ini adalah yang paling sederhana untuk dijelaskan.
Pernyataan pertama:
mendeklarasikan
p
sebagai pointer kechar
. Ketika kita mengatakan "pointer to achar
", apa artinya itu? Ini berarti bahwa nilaip
adalah alamat achar
;p
memberitahu kita di mana di memori ada beberapa ruang yang disisihkan untuk menampungchar
.Pernyataan ini juga menginisialisasi
p
untuk menunjuk ke karakter pertama dalam string literal"Hello"
. Demi latihan ini, penting untuk dipahamip
sebagai menunjuk bukan ke seluruh string, tetapi hanya untuk karakter pertama'H'
,. Bagaimanapun,p
adalah pointer ke satuchar
, bukan ke seluruh string. Nilaip
adalah alamat'H'
in"Hello"
.Kemudian Anda mengatur lingkaran:
Apa arti dari kondisi loop
*p++
? Tiga hal sedang dikerjakan di sini yang membuat ini membingungkan (setidaknya sampai keakraban muncul):++
dan tipuan*
1. Diutamakan . Pandangan sekilas pada tabel presedensi untuk operator akan memberi tahu Anda bahwa kenaikan postfix memiliki prioritas lebih tinggi (16) daripada dereferensi / tipuan (15). Ini berarti bahwa ekspresi kompleks
*p++
akan dikelompokkan sebagai:*(p++)
. Artinya,*
bagian itu akan diterapkan pada nilaip++
bagian itu. Jadi mari kita ambilp++
bagian dulu.2. Nilai ekspresi postfix . Nilai
p++
adalah nilaip
sebelum kenaikan . Jika Anda memiliki:hasilnya adalah:
karena
i++
mengevaluasii
sebelum kenaikan. Demikian pulap++
akan mengevaluasi dengan nilai saat inip
. Seperti yang kita ketahui, nilai saat inip
adalah alamat'H'
.Jadi sekarang
p++
bagian dari*p++
telah dievaluasi; ini adalah nilai saat inip
. Kemudian*
bagian itu terjadi.*(current value of p)
berarti: mengakses nilai di alamat yang dipegang olehp
. Kami tahu bahwa nilai di alamat itu adalah'H'
. Jadi ekspresi*p++
dievaluasi menjadi'H'
.Tunggu sebentar, katamu. Jika
*p++
dievaluasi'H'
, mengapa itu tidak'H'
dicetak dalam kode di atas? Di situlah efek samping masuk.3. Efek samping ekspresi postfix . Postfix
++
memiliki nilai operan saat ini, tetapi memiliki efek samping dari penambahan operan itu. Hah? Lihatlahint
kode itu lagi:Seperti disebutkan sebelumnya, hasilnya adalah:
Ketika
i++
dievaluasi pada yang pertamaprintf()
, ia dievaluasi menjadi 7. Tetapi standar C menjamin bahwa pada titik tertentu sebelum yang keduaprintf()
mulai dijalankan, efek samping dari++
operator akan terjadi. Artinya, sebelum yang keduaprintf()
terjadi,i
akan bertambah sebagai hasil dari++
operator di yang pertamaprintf()
. Omong-omong, ini adalah salah satu dari sedikit jaminan yang diberikan standar tentang waktu efek samping.Di kode Anda, kemudian, ketika ekspresi
*p++
dievaluasi, ia mengevaluasi ke'H'
. Tetapi pada saat Anda sampai pada ini:bahwa efek samping sial telah terjadi.
p
telah bertambah. Wah! Itu tidak lagi menunjuk ke'H'
, tetapi ke satu karakter masa lalu'H'
: ke'e'
, dengan kata lain. Itu menjelaskan hasil yang dikhianati:Oleh karena itu paduan suara saran yang membantu (dan akurat) di jawaban lain: untuk mencetak Pengucapan yang Diterima
"Hello"
dan bukan mitra cockney-nya, Anda memerlukan sesuatu sepertiSangat banyak untuk itu. Bagaimana dengan yang lainnya? Anda bertanya tentang arti dari ini:
Kami hanya berbicara tentang yang pertama, jadi mari kita lihat ini di kedua:
*++ptr
.Kami melihat dalam penjelasan kami sebelumnya bahwa kenaikan postfix
p++
memiliki prioritas , nilai , dan efek samping tertentu . Peningkatan awalan++p
memiliki efek samping yang sama dengan postfix-nya: ia meningkatkan operandnya dengan 1. Namun, ia memiliki prioritas yang berbeda dan nilai yang berbeda .Peningkatan awalan memiliki prioritas lebih rendah daripada postfix; ia memiliki diutamakan 15. Dengan kata lain, ia memiliki prioritas yang sama dengan operator dereference / indirection
*
. Dalam ungkapan sukayang penting bukanlah yang diutamakan: kedua operator identik dalam hal yang diutamakan. Jadi associativity menendang masuk. Peningkatan awalan dan operator tipuan memiliki associativity kanan-kiri. Karena asosiatif itu, operan
ptr
akan dikelompokkan dengan operator paling kanan++
sebelum operator lebih ke kiri*
,. Dengan kata lain, ekspresi akan dikelompokkan*(++ptr)
. Jadi, seperti halnya*ptr++
tetapi untuk alasan yang berbeda, di sini juga*
bagian akan diterapkan pada nilai++ptr
bagian.Jadi apa nilainya? Nilai ekspresi kenaikan awalan adalah nilai operan setelah kenaikan . Ini membuatnya menjadi binatang yang sangat berbeda dari operator kenaikan postfix. Katakanlah Anda memiliki:
Outputnya adalah:
... berbeda dari yang kami lihat dengan operator postfix. Demikian pula, jika Anda memiliki:
hasilnya adalah:
Apakah kamu melihat mengapa?
Sekarang kita sampai pada ekspresi ketiga yang Anda tanyakan
++*ptr
,. Sebenarnya itu yang paling sulit di antara semuanya. Kedua operator memiliki prioritas yang sama, dan asosiasi kiri-kanan. Ini berarti ekspresi akan dikelompokkan++(*ptr)
. Bagian++
akan diterapkan pada nilai*ptr
bagian tersebut.Jadi, jika kita memiliki:
hasil yang mengejutkan dari egoisme adalah:
Apa?! Oke, jadi
*p
bagian itu akan dievaluasi'H'
. Kemudian++
permainan mulai, pada titik mana, itu akan diterapkan ke'H'
, bukan ke pointer sama sekali! Apa yang terjadi ketika Anda menambahkan 1'H'
? Anda mendapatkan 1 ditambah nilai ASCII'H'
, 72; Anda mendapatkan 73. Menyatakan bahwa sebagaichar
, dan Anda mendapatkanchar
dengan nilai ASCII dari 73:'I'
.Itu menangani tiga ekspresi yang Anda tanyakan dalam pertanyaan Anda. Ini yang lain, disebutkan dalam komentar pertama untuk pertanyaan Anda:
Yang itu juga menarik. Jika Anda memiliki:
itu akan memberi Anda hasil yang antusias ini:
Apa yang sedang terjadi? Sekali lagi, ini masalah prioritas , nilai ekspresi , dan efek samping . Karena tanda kurung,
*p
bagian diperlakukan sebagai ekspresi utama. Ekspresi primer mengalahkan segalanya; mereka dievaluasi terlebih dahulu. Dan*p
, seperti yang Anda tahu, mengevaluasi'H'
. Sisa ekspresi,++
bagian, diterapkan ke nilai itu. Jadi, dalam hal ini,(*p)++
jadilah'H'++
.Apa nilainya
'H'++
? Jika Anda mengatakan'I'
, Anda sudah lupa (sudah!) Diskusi kami tentang nilai vs efek samping dengan kenaikan postfix. Ingat,'H'++
evaluasi dengan nilai saat ini dari'H'
. Jadi yang pertamaprintf()
akan dicetak'H'
. Kemudian, sebagai efek samping , itu'H'
akan bertambah'I'
. Yang keduaprintf()
mencetak itu'I'
. Dan Anda memiliki ucapan ceria.Baiklah, tetapi dalam dua kasus terakhir, mengapa saya perlu
Kenapa aku tidak bisa memiliki sesuatu seperti itu
Karena
"Hello"
merupakan string literal. Jika Anda mencoba++*p
, Anda mencoba mengubah'H'
string ke'I'
, membuat keseluruhan string"Iello"
. Dalam C, string literal hanya-baca; mencoba memodifikasinya memunculkan perilaku yang tidak jelas."Iello"
tidak terdefinisi dalam bahasa Inggris juga, tapi itu hanya kebetulan.Sebaliknya, Anda tidak bisa
Kenapa tidak? Karena dalam contoh ini,
p
adalah array. Array bukan nilai-l yang dapat dimodifikasi; Anda tidak dapat mengubah di manap
poin dengan kenaikan sebelum atau sesudah kenaikan atau penurunan, karena nama array berfungsi seolah-olah itu adalah pointer konstan. (Bukan itu sebenarnya; itu hanya cara yang nyaman untuk melihatnya.)Singkatnya, berikut adalah tiga hal yang Anda tanyakan:
Dan inilah yang keempat, setiap kesenangan sebanyak tiga lainnya:
Yang pertama dan kedua akan crash jika
ptr
sebenarnya merupakan pengidentifikasi array. Yang ketiga dan keempat akan crash jikaptr
menunjuk ke string literal.Itu dia. Saya harap semuanya kristal sekarang. Anda telah menjadi pendengar yang hebat, dan saya akan berada di sini sepanjang minggu.
sumber
Misalkan
ptr
menunjuk ke elemen array ke-iarr
.*ptr++
mengevaluasi kearr[i]
dan setptr
untuk menunjuk ke elemen (i +1) dariarr
. Ini setara dengan*(ptr++)
.*++ptr
setptr
untuk menunjuk ke elemen (i +1)arr
dan mengevaluasi kearr[i+1]
. Ini setara dengan*(++ptr)
.++*ptr
meningkatarr[i]
satu dan mengevaluasi nilainya meningkat; pointerptr
dibiarkan tak tersentuh. Ini setara dengan++(*ptr)
.Ada juga satu lagi, tetapi Anda membutuhkan tanda kurung untuk menuliskannya:
(*ptr)++
bertambaharr[i]
satu dan mengevaluasi nilainya sebelum ditingkatkan; pointerptr
lagi dibiarkan tak tersentuh.Sisanya, Anda bisa mengetahui sendiri; itu juga dijawab oleh @Jaguar.
sumber
*ptr++ : post increment a pointer ptr
*++ptr : Pre Increment a pointer ptr
++*ptr : preincrement the value at ptr location
Baca di sini tentang operator kenaikan sebelum dan sesudah kenaikan
Ini akan memberikan
Hello
sebagai outputsumber
Hello
Kondisi dalam loop Anda buruk:
Sama dengan
Dan itu salah, ini seharusnya:
*ptr++
sama dengan*(ptr++)
, yaitu:*++ptr
sama dengan*(++ptr)
, yaitu:++*ptr
sama dengan++(*ptr)
, yaitu:sumber
Anda benar tentang prioritas, perhatikan bahwa
*
prioritas lebih tinggi daripada kenaikan awalan, tetapi tidak lebih dari kenaikan postfix. Begini cara pemecahannya:*ptr++
- pergi dari kiri ke kanan, melakukan dereferensi pointer, dan kemudian meningkatkan nilai pointer (bukan apa yang ditunjukkan, karena didahulukan dari postfix atas dereference)*++ptr
- increment the pointer dan kemudian dereference, ini karena awalan dan dereference memiliki prioritas yang sama dan mereka dievaluasi dalam urutan kanan-ke-kiri++*ptr
- mirip dengan yang di atas dalam hal presedensi, sekali lagi pergi dari kanan ke kiri dalam rangka dereferensi pointer dan kemudian meningkatkan apa yang menunjuk pointer. Harap perhatikan bahwa dalam kasus Anda, ini akan menyebabkan perilaku tidak terdefinisi karena Anda mencoba mengubah variabel read-only (char* p = "Hello";
).sumber
Saya akan menambahkan pendapat saya karena sementara jawaban lainnya benar saya pikir mereka kehilangan sesuatu.
cara
Dimana sebagai
cara
Sangat penting untuk memahami bahwa kenaikan pasca (dan pasca penurunan) berarti
Mengapa itu penting? Nah di C itu tidak begitu penting. Dalam C + +
ptr
mungkin tipe kompleks seperti iterator. Sebagai contohDalam hal ini, karena
it
jenis yang kompleksit++
mungkin memiliki efek samping karenatemp
pembuatannya. Tentu saja jika Anda beruntung, kompiler akan mencoba untuk membuang kode yang tidak diperlukan tetapi jika konstruktor atau destruktor iterator melakukan apa pun makait++
akan menunjukkan efek tersebut ketika dibuattemp
.Pendek dari apa yang saya coba katakan adalah Write What You Mean . Jika Anda maksud ptr kenaikan maka tulis
++ptr
tidakptr++
. Jika Anda maksudtemp = ptr, ptr += 1, temp
maka tulislahptr++
sumber
Ini sama dengan:
Jadi nilai objek yang ditunjuk oleh
ptr
diambil, laluptr
ditambahkan.Ini sama dengan:
Jadi pointer
ptr
bertambah, maka objek yang ditunjuk olehptr
dibaca.Ini sama dengan:
Jadi objek yang ditunjuk oleh
ptr
bertambah;ptr
itu sendiri tidak berubah.sumber
postfix dan prefix memiliki prioritas lebih tinggi daripada dereference
* ptr ++ di sini memposting kenaikan ptr dan kemudian menunjuk ke nilai ptr baru
* ++ ptr di sini Tinju Pra Peningkatan kemudian menunjuk ke nilai baru ptr
++ * ptr di sini pertama-tama mendapatkan nilai ptr menunjuk ke dan meningkatkan vlaue itu
sumber
Ekspresi Pointer: * ptr ++, * ++ ptr dan ++ * ptr:
Catatan : pointer harus diinisialisasi dan harus memiliki alamat yang valid. Karena dalam RAM selain dari program kami (a.out) ada banyak lagi program yang berjalan secara bersamaan yaitu jika Anda mencoba mengakses beberapa memori yang tidak dicadangkan untuk OS Anda akan melalui kesalahan Segmentasi.
Sebelum menjelaskan ini, mari pertimbangkan contoh sederhana?
menganalisis output dari kode di atas, saya harap Anda mendapatkan output dari kode di atas. Satu hal yang jelas dari kode di atas adalah bahwa nama penunjuk ( ptr ) berarti kita berbicara tentang alamat dan * ptr berarti kita berbicara tentang nilai / data.
KASUS 1 : * ptr ++, * ++ ptr, * (ptr ++) dan * (++ ptr):
Semua sintaks yang disebutkan di atas mirip, dalam semua
address gets incremented
tapi bagaimana alamat bertambah itu berbeda.Catatan : untuk menyelesaikan ekspresi apa pun, cari tahu berapa banyak operator yang ada dalam ekspresi, kemudian cari tahu prioritas operator. Saya beberapa operator memiliki prioritas yang sama kemudian memeriksa urutan evolusi atau asosiasi yang dapat kanan (R) ke kiri (L) atau kiri ke kanan.
* ptr ++ : Di sini ada 2 operator yaitu de-reference (*) dan ++ (increment). Keduanya memiliki prioritas yang sama kemudian memeriksa asosiatif yaitu R to L. Jadi, mulailah menyelesaikan dari Kanan ke Kiri, operator apa pun yang lebih dulu.
* ptr ++ : first ++ datang sambil menyelesaikan dari R ke L, jadi alamat bertambah tetapi post kenaikannya.
* ++ ptr : Sama seperti yang pertama di sini juga alamat bertambah tetapi kenaikannya sebelum.
* (ptr ++) : Di sini ada 3 operator, di antaranya pengelompokan () yang memiliki prioritas tertinggi, Jadi ptr ++ pertama yang dipecahkan yaitu alamat akan bertambah tetapi dikirim.
* (++ ptr) : Sama seperti kasus di atas di sini juga alamat akan bertambah tetapi sebelum kenaikan.
KASUS 2 : ++ * ptr, ++ (* ptr), (* ptr) ++:
Semua sintaks yang disebutkan di atas serupa, dalam semua nilai / data bertambah tetapi bagaimana nilai diubah yang berbeda.
++ * ptr : first * datang sambil menyelesaikan dari R ke L, jadi nilainya berubah tetapi kenaikannya sebelum.
++ (* ptr) : Sama seperti huruf di atas, nilainya diubah.
(* ptr) ++ : Di sini ada 3 operator, di antaranya pengelompokan () memiliki prioritas tertinggi, Inside () * ptr ada, Jadi pertama * ptr diselesaikan yaitu nilai bertambah tetapi posting.
Catatan : ++ * ptr dan * ptr = * ptr + 1 keduanya sama, dalam kedua nilai kasus akan berubah. ++ * ptr: hanya 1 instruksi (INC) yang digunakan, nilai langsung akan berubah dalam satu tembakan. * ptr = * ptr + 1: di sini nilai pertama akan bertambah (INC) dan kemudian ditugaskan (MOV).
Untuk memahami semua sintaks kenaikan yang berbeda pada pointer, mari pertimbangkan kode sederhana ini:
Dalam kode di atas, cobalah untuk mengomentari / membatalkan komentar komentar dan menganalisis output.
Pointer sebagai Constant : tidak ada cara dimana Anda dapat menjadikan pointer sebagai konstan, hanya sedikit yang saya sebutkan di sini.
1) const int * p OR int const * p : Berikut
value
adalah konstan , alamat tidak konstan yaitu dimana p menunjuk? Beberapa alamat? Pada alamat itu berapa nilainya? Beberapa nilai bukan? Nilai itu konstan, Anda tidak bisa mengubah nilai itu tetapi di mana pointer menunjuk? Beberapa alamat bukan? Itu bisa menunjuk ke alamat lain juga.Untuk memahami ini, mari pertimbangkan kode di bawah ini:
Cobalah untuk menganalisis output dari kode di atas
2) int const * p : itu disebut
**constant pointe**r
'yaituaddress is constant but value is not constant
. Di sini Anda tidak diperbolehkan mengubah alamat tetapi Anda dapat mengubah nilainya.Catatan : pointer konstan (huruf di atas) harus menginisialisasi sambil menyatakan sendiri.
Untuk memahami ini mari kita periksa kode sederhana.
Dalam kode di atas, jika Anda mengamati bahwa tidak ada ++ * p atau * p ++ Jadi Anda mungkin berpikir ini adalah kasus sederhana karena kami tidak mengubah alamat atau nilai tetapi akan menghasilkan kesalahan. Mengapa Alasan saya sebutkan dalam komentar.
Jadi, apa solusi dari masalah ini?
untuk lebih lanjut tentang kasus ini mari kita perhatikan contoh di bawah ini.
3) const int * const p : Di sini alamat dan nilainya konstan .
Untuk memahami ini mari kita periksa kode di bawah ini
sumber
++*p
berarti Anda mencoba untuk menambah nilai ASCII*p
yang manaAnda tidak dapat menambah nilainya karena ini adalah konstanta sehingga Anda akan mendapatkan kesalahan
Sedangkan untuk loop sementara Anda, loop berjalan hingga
*p++
mencapai ujung string di mana ada karakter'\0'
(NULL).Sekarang karena
*p++
melompati karakter pertama Anda hanya akan mendapatkan output Anda mulai dari karakter kedua.Kode berikut tidak akan menghasilkan apa-apa karena while memiliki
'\0'
Kode berikut akan memberi Anda output yang sama dengan kode berikutnya yaitu ello.
...................................
sumber