Inilah penjelasan terperinci yang saya harap akan membantu. Mari kita mulai dengan program Anda, karena ini adalah yang paling sederhana untuk dijelaskan.
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
Pernyataan pertama:
const char* p = "Hello";
mendeklarasikan p
sebagai pointer ke char
. Ketika kita mengatakan "pointer to a char
", apa artinya itu? Ini berarti bahwa nilai p
adalah alamat a char
; p
memberitahu kita di mana di memori ada beberapa ruang yang disisihkan untuk menampung char
.
Pernyataan ini juga menginisialisasi p
untuk menunjuk ke karakter pertama dalam string literal "Hello"
. Demi latihan ini, penting untuk dipahami p
sebagai menunjuk bukan ke seluruh string, tetapi hanya untuk karakter pertama 'H'
,. Bagaimanapun, p
adalah pointer ke satu char
, bukan ke seluruh string. Nilai p
adalah alamat 'H'
in "Hello"
.
Kemudian Anda mengatur lingkaran:
while (*p++)
Apa arti dari kondisi loop *p++
? Tiga hal sedang dikerjakan di sini yang membuat ini membingungkan (setidaknya sampai keakraban muncul):
- Diutamakan dari dua operator, postfix
++
dan tipuan*
- Nilai ekspresi kenaikan postfix
- Efek samping dari ekspresi kenaikan postfix
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 nilai p++
bagian itu. Jadi mari kita ambil p++
bagian dulu.
2. Nilai ekspresi postfix . Nilai p++
adalah nilai p
sebelum kenaikan . Jika Anda memiliki:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
hasilnya adalah:
7
8
karena i++
mengevaluasi i
sebelum kenaikan. Demikian pula p++
akan mengevaluasi dengan nilai saat ini p
. Seperti yang kita ketahui, nilai saat ini p
adalah alamat 'H'
.
Jadi sekarang p++
bagian dari *p++
telah dievaluasi; ini adalah nilai saat ini p
. Kemudian *
bagian itu terjadi. *(current value of p)
berarti: mengakses nilai di alamat yang dipegang oleh p
. 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? Lihatlah int
kode itu lagi:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
Seperti disebutkan sebelumnya, hasilnya adalah:
7
8
Ketika i++
dievaluasi pada yang pertama printf()
, ia dievaluasi menjadi 7. Tetapi standar C menjamin bahwa pada titik tertentu sebelum yang kedua printf()
mulai dijalankan, efek samping dari ++
operator akan terjadi. Artinya, sebelum yang kedua printf()
terjadi, i
akan bertambah sebagai hasil dari ++
operator di yang pertama printf()
. 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:
printf ("%c", *p)
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:
ello
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 seperti
while (*p)
printf ("%c", *p++);
Sangat banyak untuk itu. Bagaimana dengan yang lainnya? Anda bertanya tentang arti dari ini:
*ptr++
*++ptr
++*ptr
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 suka
*++ptr
yang 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:
int i = 7;
printf ("%d\n", ++i);
printf ("%d\n", i);
Outputnya adalah:
8
8
... berbeda dari yang kami lihat dengan operator postfix. Demikian pula, jika Anda memiliki:
const char* p = "Hello";
printf ("%c ", *p); // note space in format string
printf ("%c ", *++p); // value of ++p is p after the increment
printf ("%c ", *p++); // value of p++ is p before the increment
printf ("%c ", *p); // value of p has been incremented as a side effect of p++
hasilnya adalah:
H e e l // good dog
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:
char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);
hasil yang mengejutkan dari egoisme adalah:
I
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 sebagai char
, dan Anda mendapatkan char
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:
(*ptr)++
Yang itu juga menarik. Jika Anda memiliki:
char q[] = "Hello";
char* p = q;
printf ("%c", (*p)++);
printf ("%c\n", *p);
itu akan memberi Anda hasil yang antusias ini:
HI
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 pertama printf()
akan dicetak 'H'
. Kemudian, sebagai efek samping , itu 'H'
akan bertambah 'I'
. Yang kedua printf()
mencetak itu 'I'
. Dan Anda memiliki ucapan ceria.
Baiklah, tetapi dalam dua kasus terakhir, mengapa saya perlu
char q[] = "Hello";
char* p = q;
Kenapa aku tidak bisa memiliki sesuatu seperti itu
/*const*/ char* p = "Hello";
printf ("%c", ++*p); // attempting to change string literal!
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
char p[] = "Hello";
printf ("%c", *++p); // attempting to modify value of array identifier!
Kenapa tidak? Karena dalam contoh ini, p
adalah array. Array bukan nilai-l yang dapat dimodifikasi; Anda tidak dapat mengubah di mana p
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:
*ptr++ // effectively dereferences the pointer, then increments the pointer
*++ptr // effectively increments the pointer, then dereferences the pointer
++*ptr // effectively dereferences the pointer, then increments dereferenced value
Dan inilah yang keempat, setiap kesenangan sebanyak tiga lainnya:
(*ptr)++ // effectively forces a dereference, then increments dereferenced value
Yang pertama dan kedua akan crash jika ptr
sebenarnya merupakan pengidentifikasi array. Yang ketiga dan keempat akan crash jika ptr
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.
(*ptr)++
(tanda kurung diperlukan untuk disambiguasi dari*ptr++
)