Saya baru-baru ini senang menjelaskan pointer ke pemula pemrograman C dan menemukan kesulitan berikut. Mungkin tidak tampak seperti masalah sama sekali jika Anda sudah tahu cara menggunakan pointer, tetapi cobalah untuk melihat contoh berikut dengan pikiran yang jernih:
int foo = 1;
int *bar = &foo;
printf("%p\n", (void *)&foo);
printf("%i\n", *bar);
Bagi pemula absolut hasilnya mungkin mengejutkan. Pada baris 2 dia baru saja menyatakan * bar sebagai & foo, tetapi pada baris 4 ternyata * bar sebenarnya foo bukan & foo!
Kebingungan, Anda mungkin mengatakan, berasal dari ambiguitas simbol *: Pada baris 2 digunakan untuk mendeklarasikan pointer. Pada baris 4 digunakan sebagai operator unary yang mengambil nilai titik penunjuk. Dua hal yang berbeda, bukan?
Namun, "penjelasan" ini sama sekali tidak membantu pemula. Ini memperkenalkan konsep baru dengan menunjukkan perbedaan yang halus. Ini bukan cara yang tepat untuk mengajarkannya.
Jadi, bagaimana Kernighan dan Ritchie menjelaskannya?
Operator unary * adalah operator tipuan atau dereferencing; ketika diterapkan pada sebuah pointer, ia mengakses objek yang ditunjuk oleh pointer itu. [...]
Deklarasi pointer ip,
int *ip
dimaksudkan sebagai mnemonik; dikatakan bahwa ekspresinya*ip
adalah int. Sintaks deklarasi untuk variabel meniru sintaks ekspresi di mana variabel mungkin muncul .
int *ip
harus dibaca seperti " *ip
akan mengembalikan sebuah int
"? Tetapi mengapa kemudian penugasan setelah deklarasi tidak mengikuti pola itu? Bagaimana jika seorang pemula ingin menginisialisasi variabel? int *ip = 1
(baca: *ip
akan mengembalikan int
dan int
is 1
) tidak akan berfungsi seperti yang diharapkan. Model konseptual sepertinya tidak koheren. Apakah saya melewatkan sesuatu di sini?
Sunting: Ini mencoba merangkum jawaban di sini .
*
dalam sebuah deklarasi adalah makna token "menyatakan pointer", dalam ekspresi itu adalah operator dereference, dan bahwa keduanya mewakili hal-hal berbeda yang kebetulan memiliki simbol yang sama (sama seperti operator perkalian - simbol yang sama, makna yang berbeda). Ini membingungkan, tetapi sesuatu yang berbeda dari keadaan sebenarnya akan menjadi lebih buruk.int* bar
membuatnya lebih jelas bahwa bintang itu sebenarnya bagian dari tipe, bukan bagian dari pengenal. Tentu saja ini membawa Anda ke masalah yang berbeda dengan hal-hal yang tidak intuitif sepertiint* a, b
.*
dapat memiliki dua arti berbeda tergantung pada konteksnya. Sama seperti huruf yang sama dapat diucapkan berbeda tergantung pada kata itu di mana membuatnya sulit untuk belajar berbicara banyak bahasa. Jika setiap konsep / operasi memiliki simbolnya sendiri, kita akan membutuhkan keyboard yang lebih besar, jadi simbol tersebut didaur ulang ketika masuk akal untuk melakukannya.int* p
), sembari memperingatkan siswa Anda agar tidak menggunakan banyak deklarasi di baris yang sama ketika pointer terlibat. Ketika siswa telah sepenuhnya memahami konsep pointer, jelaskan kepada siswa bahwaint *p
sintaks is adalah setara dan kemudian jelaskan masalahnya dengan beberapa deklarasi.Jawaban:
Agar siswa Anda memahami makna
*
simbol dalam konteks yang berbeda, mereka harus terlebih dahulu memahami bahwa konteksnya memang berbeda. Begitu mereka memahami bahwa konteksnya berbeda (yaitu perbedaan antara sisi kiri dari tugas dan ekspresi umum) itu tidak terlalu banyak lompatan kognitif untuk memahami apa perbedaannya.Pertama, jelaskan bahwa deklarasi variabel tidak boleh berisi operator (tunjukkan hal ini dengan menunjukkan bahwa menempatkan a
-
atau+
simbol dalam deklarasi variabel hanya menyebabkan kesalahan). Kemudian lanjutkan untuk menunjukkan bahwa ekspresi (yaitu di sisi kanan penugasan) dapat berisi operator. Pastikan siswa memahami bahwa ekspresi dan deklarasi variabel adalah dua konteks yang sama sekali berbeda.Ketika mereka memahami bahwa konteksnya berbeda, Anda bisa menjelaskan bahwa ketika
*
simbol ada dalam deklarasi variabel di depan pengenal variabel, itu berarti 'mendeklarasikan variabel ini sebagai penunjuk'. Kemudian Anda dapat menjelaskan bahwa ketika digunakan dalam ekspresi (sebagai operator unary)*
simbol adalah 'operator dereferensi' dan itu berarti 'nilai pada alamat' daripada makna sebelumnya.Untuk benar-benar meyakinkan siswa Anda, jelaskan bahwa pencipta C dapat menggunakan simbol apa pun untuk berarti operator dereference (yaitu mereka dapat menggunakan
@
sebagai gantinya) tetapi untuk alasan apa pun mereka membuat keputusan desain untuk digunakan*
.Secara keseluruhan, tidak ada jalan lain untuk menjelaskan bahwa konteksnya berbeda. Jika siswa tidak memahami konteksnya berbeda, mereka tidak dapat memahami mengapa
*
simbol tersebut dapat berarti hal yang berbeda.sumber
Alasan mengapa tulisan cepat:
dalam contoh Anda dapat membingungkan adalah bahwa mudah untuk salah membaca sebagai setara dengan:
padahal sebenarnya artinya:
Ditulis seperti ini, dengan deklarasi variabel dan penugasan dipisahkan, tidak ada potensi kebingungan, dan penggunaan ↔ paralelisme deklarasi yang dijelaskan dalam kutipan K&R Anda berfungsi dengan baik:
Baris pertama mendeklarasikan variabel
bar
, sehingga*bar
merupakanint
.Baris kedua memberikan alamat
foo
tobar
, membuat*bar
(anint
) alias untukfoo
(juga anint
).Saat memperkenalkan sintaks pointer C kepada pemula, mungkin awalnya membantu untuk tetap berpegang pada gaya ini memisahkan deklarasi pointer dari tugas, dan hanya memperkenalkan sintaks steno gabungan (dengan peringatan yang tepat tentang potensi kebingungan) setelah konsep dasar penggunaan pointer di C telah diinternalisasi secara memadai.
sumber
typedef
.typedef int *p_int;
berarti bahwa variabel tipep_int
memiliki properti yang*p_int
adalahint
. Lalu kita punyap_int bar = &foo;
. Mendorong siapa pun untuk membuat data yang tidak diinisialisasi dan kemudian menetapkannya sebagai masalah kebiasaan bawaan tampaknya ... seperti ide yang buruk.int a[2] = {47,11};
, itu bukan inisialisasi elemen (tidak ada) lebih mudaha[2]
.*
harus menjadi bagian dari tipe, tidak terikat pada variabel, dan kemudian Anda akan dapat menulisint* foo_ptr, bar_ptr
untuk mendeklarasikan dua petunjuk. Tetapi sebenarnya menyatakan pointer dan integer.Pendek pada deklarasi
Sangat menyenangkan untuk mengetahui perbedaan antara deklarasi dan inisialisasi. Kami mendeklarasikan variabel sebagai tipe dan menginisialisasi mereka dengan nilai. Jika kita melakukan keduanya sekaligus, kita sering menyebutnya definisi.
1.
int a; a = 42;
Kami mendeklarasikan sebuah
int
bernama sebuah . Lalu kami menginisialisasi dengan memberikan nilai42
.2.
int a = 42;
Kami mendeklarasikan dan memberi
int
nama a dan memberikannya nilai 42. Ini diinisialisasi dengan42
. Definisi.3.
a = 43;
Ketika kita menggunakan variabel, kita katakan kita mengoperasikannya .
a = 43
adalah operasi penugasan. Kami menetapkan angka 43 ke variabel a.Dengan berkata
kami menyatakan bilah sebagai penunjuk ke int. Dengan berkata
kami mendeklarasikan bilah dan menginisialisasi dengan alamat foo .
Setelah kita menginisialisasi bilah, kita dapat menggunakan operator yang sama, tanda bintang, untuk mengakses dan beroperasi pada nilai foo . Tanpa operator, kami mengakses dan beroperasi pada alamat yang ditunjuk oleh pointer.
Selain itu saya membiarkan gambar berbicara.
Apa
ASCIIMASI yang disederhanakan tentang apa yang sedang terjadi. (Dan di sini versi pemain jika Anda ingin berhenti, dll.)
sumber
Pernyataan ke-2
int *bar = &foo;
dapat dilihat secara foto dalam memori sebagai,Sekarang
bar
adalah pointer dari jenisint
yang mengandung alamat&
darifoo
. Menggunakan operator unary,*
kami menghormati untuk mengambil nilai yang terkandung dalam 'foo' dengan menggunakan pointerbar
.EDIT : Pendekatan saya dengan pemula adalah menjelaskan
memory address
variabel yaituMemory Address:
Setiap variabel memiliki alamat yang dikaitkan dengan itu yang disediakan oleh OS. Diint a;
,&a
adalah alamat variabela
.Lanjutkan menjelaskan tipe dasar variabel dalam
C
bentuk,Types of variables:
Variabel dapat menyimpan nilai dari masing-masing jenis tetapi tidak alamat.Introducing pointers:
Seperti yang dikatakan variabel di atas, misalnyaDimungkinkan penetapan
b = a
tetapi tidakb = &a
, karena variabelb
dapat menyimpan nilai tetapi tidak mengatasi, Oleh karena itu kami memerlukan Pointer .Pointer or Pointer variables :
Jika suatu variabel berisi alamat itu dikenal sebagai variabel pointer. Gunakan*
dalam deklarasi untuk menginformasikan bahwa itu adalah pointer.sumber
int *ip
sebagai "ip adalah pointer (*) dari tipe int" Anda mendapat masalah saat membaca sesuatu sepertix = (int) *ip
.x = (int) *ip;
, dapatkan nilainya dengan dereferencing pointerip
dan berikan nilainyaint
dari tipe apa punip
.int* bar = &foo;
membuat beban lebih masuk akal. Ya, saya tahu ini menyebabkan masalah ketika Anda mendeklarasikan banyak pointer dalam satu deklarasi. Tidak, saya pikir itu tidak penting sama sekali.Melihat jawaban dan komentar di sini, tampaknya ada kesepakatan umum bahwa sintaks yang dipermasalahkan dapat membingungkan bagi pemula. Sebagian besar dari mereka mengusulkan sesuatu seperti ini:
Anda dapat menulis
int* bar
alih-alihint *bar
menyoroti perbedaannya. Ini berarti Anda tidak akan mengikuti pendekatan K&R "declaration mimics use", tetapi pendekatan Stroustrup C ++ :Kami tidak menyatakan
*bar
sebagai bilangan bulat. Kami menyatakanbar
sebagaiint*
. Jika kita ingin menginisialisasi variabel yang baru dibuat di baris yang sama, jelas kita sedang berurusanbar
, bukan*bar
.int* bar = &foo;
Kekurangannya:
int* foo, bar
vsint *foo, *bar
).Sunting: Pendekatan berbeda yang telah disarankan, adalah mengikuti cara K&R "meniru", tetapi tanpa sintaks "steno" (lihat di sini ). Segera setelah Anda menghilangkan melakukan deklarasi dan penugasan di baris yang sama , semuanya akan terlihat jauh lebih masuk akal.
Namun, cepat atau lambat siswa harus berurusan dengan pointer sebagai argumen fungsi. Dan pointer sebagai tipe pengembalian. Dan petunjuk fungsi. Anda harus menjelaskan perbedaan antara
int *func();
danint (*func)();
. Saya pikir cepat atau lambat segalanya akan berantakan. Dan mungkin lebih cepat lebih baik daripada nanti.sumber
Ada alasan mengapa gaya K&R nikmat
int *p
dan gaya gaya Stroustrupint* p
; keduanya valid (dan berarti hal yang sama) di setiap bahasa, tetapi seperti yang dikatakan Stroustrup:Sekarang, karena Anda mencoba mengajar C di sini, itu akan menyarankan Anda harus lebih menekankan ekspresi jenis itu, tetapi beberapa orang dapat lebih mudah grok satu penekanan lebih cepat daripada yang lain, dan itu tentang mereka daripada bahasa.
Oleh karena itu beberapa orang akan merasa lebih mudah untuk memulai dengan gagasan bahwa an
int*
adalah hal yang berbeda daripada anint
dan pergi dari sana.Jika seseorang cepat grok cara untuk melihat hal itu yang menggunakan
int* bar
untuk memilikibar
sebagai hal yang tidak int, tapi pointer keint
, maka mereka akan segera melihat bahwa*bar
ini melakukan sesuatu untukbar
, dan sisanya akan mengikuti. Setelah Anda selesai melakukannya nanti Anda bisa menjelaskan mengapa C coder cenderung lebih sukaint *bar
.Atau tidak. Jika ada satu cara di mana setiap orang pertama kali memahami konsep itu, Anda tidak akan memiliki masalah sejak awal, dan cara terbaik untuk menjelaskannya kepada satu orang tidak akan selalu menjadi cara terbaik untuk menjelaskannya kepada orang lain.
sumber
int* p = &a
maka kita bisa melakukannyaint* r = *p
. Saya cukup yakin dia meliputnya di Desain dan Evolusi C ++ , tapi itu sudah lama sejak saya membacanya, dan saya dengan bodohnya mencondongkan salinan saya ke seseorang.int& r = *p
. Dan saya yakin peminjam masih mencoba untuk mencerna buku itu.Var A, B: ^Integer;
membuat jelas bahwa tipe "pointer to integer" berlaku untuk keduanyaA
danB
. MenggunakanK&R
gayaint *a, *b
juga bisa diterapkan; tetapi deklarasi sepertiint* a,b;
, bagaimanapun, terlihat seolah-olaha
danb
keduanya dinyatakan sebagaiint*
, tetapi dalam kenyataannya menyatakana
sebagaiint*
danb
sebagaiint
.tl; dr:
A: jangan. Jelaskan pointer ke pemula, dan tunjukkan kepada mereka bagaimana untuk mewakili konsep pointer mereka dalam sintaks C setelah.
Sintaksis IMO the C tidak buruk, tetapi tidak bagus juga: itu bukan halangan yang bagus jika Anda sudah mengerti petunjuk, atau bantuan apa pun dalam mempelajarinya.
Oleh karena itu: mulailah dengan menjelaskan petunjuk, dan pastikan mereka benar-benar memahaminya:
Jelaskan dengan diagram kotak-dan-panah. Anda dapat melakukannya tanpa alamat hex, jika tidak relevan, cukup tunjukkan panah yang menunjuk ke kotak lain, atau ke beberapa simbol nul.
Jelaskan dengan pseudocode: cukup tulis alamat foo dan nilai yang disimpan di bar .
Kemudian, ketika pemula Anda mengerti apa itu pointer, dan mengapa, dan bagaimana menggunakannya; lalu perlihatkan pemetaan ke sintaks C.
Saya menduga alasan mengapa teks K&R tidak memberikan model konseptual adalah bahwa mereka sudah memahami petunjuk , dan mungkin mengasumsikan setiap programmer kompeten lainnya pada saat itu juga melakukannya. Mnemonic hanyalah pengingat pemetaan dari konsep yang dipahami dengan baik, ke sintaksis.
sumber
Masalah ini agak membingungkan ketika mulai belajar C.
Berikut adalah prinsip-prinsip dasar yang mungkin membantu Anda memulai:
Hanya ada beberapa tipe dasar di C:
char
: nilai integer dengan ukuran 1 byte.short
: nilai integer dengan ukuran 2 byte.long
: nilai integer dengan ukuran 4 byte.long long
: nilai integer dengan ukuran 8 byte.float
: nilai non-integer dengan ukuran 4 byte.double
: nilai non-integer dengan ukuran 8 byte.Perhatikan bahwa ukuran setiap jenis umumnya ditentukan oleh kompiler dan bukan oleh standar.
Jenis integer
short
,long
danlong long
biasanya diikuti olehint
.Ini bukan keharusan, namun, dan Anda dapat menggunakannya tanpa
int
.Atau, Anda bisa saja menyatakan
int
, tetapi itu mungkin ditafsirkan berbeda oleh kompiler yang berbeda.Jadi untuk meringkas ini:
short
samashort int
tetapi tidak harus sama denganint
.long
samalong int
tetapi tidak harus sama denganint
.long long
samalong long int
tetapi tidak harus sama denganint
.Pada kompiler yang diberikan,
int
apakahshort int
ataulong int
ataulong long int
.Jika Anda mendeklarasikan variabel dari beberapa jenis, maka Anda juga dapat mendeklarasikan variabel lain yang menunjuk padanya.
Sebagai contoh:
int a;
int* b = &a;
Jadi intinya, untuk setiap tipe dasar, kami juga memiliki tipe pointer yang sesuai.
Misalnya:
short
danshort*
.Ada dua cara untuk "melihat" variabel
b
(itulah yang mungkin membingungkan kebanyakan pemula) :Anda dapat mempertimbangkan
b
sebagai variabel tipeint*
.Anda dapat mempertimbangkan
*b
sebagai variabel tipeint
.Oleh karena itu, beberapa orang akan menyatakan
int* b
, sedangkan yang lain akan menyatakanint *b
.Tetapi faktanya adalah bahwa kedua deklarasi ini identik (spasi tidak berarti).
Anda bisa menggunakan
b
sebagai penunjuk ke nilai integer, atau*b
sebagai nilai integer runcing yang sebenarnya.Anda bisa mendapatkan (baca) nilai runcing:
int c = *b
.Dan Anda dapat mengatur (write) nilai runcing:
*b = 5
.Pointer dapat menunjuk ke alamat memori apa pun, dan tidak hanya ke alamat beberapa variabel yang telah Anda nyatakan sebelumnya. Namun, Anda harus berhati-hati saat menggunakan pointer untuk mendapatkan atau mengatur nilai yang terletak di alamat memori yang ditunjuk.
Sebagai contoh:
int* a = (int*)0x8000000;
Di sini, kami memiliki variabel yang
a
menunjuk ke alamat memori 0x8000000.Jika alamat memori ini tidak dipetakan dalam ruang memori program Anda, maka setiap operasi membaca atau menulis menggunakan
*a
kemungkinan besar akan menyebabkan program Anda macet, karena pelanggaran akses memori.Anda dapat dengan aman mengubah nilai
a
, tetapi Anda harus sangat berhati-hati mengubah nilainya*a
.Ketik
void*
luar biasa karena tidak memiliki "tipe nilai" yang sesuai yang dapat digunakan (yaitu, Anda tidak dapat mendeklarasikanvoid a
). Tipe ini hanya digunakan sebagai penunjuk umum ke alamat memori, tanpa menentukan jenis data yang berada di alamat itu.sumber
Mungkin melangkah sedikit saja membuatnya lebih mudah:
Mintalah mereka memberi tahu Anda apa yang mereka harapkan dari output di setiap baris, kemudian minta mereka menjalankan program dan melihat apa yang muncul. Jelaskan pertanyaan mereka (versi telanjang di sana pasti akan meminta beberapa - tetapi Anda dapat khawatir tentang gaya, ketegasan dan portabilitas nanti). Kemudian, sebelum pikiran mereka menjadi bubur karena terlalu banyak berpikir atau mereka menjadi zombie setelah makan siang, tulislah sebuah fungsi yang mengambil nilai, dan fungsi yang sama yang mengambil pointer.
Dalam pengalaman saya, itu mengatasi bahwa "mengapa ini dicetak seperti itu?" punuk, dan kemudian segera menunjukkan mengapa hal ini berguna dalam parameter fungsi dengan memainkan langsung (sebagai pembuka untuk beberapa materi dasar K&R seperti parsing string / pemrosesan array) yang membuat pelajaran tidak hanya masuk akal tetapi tetap.
Langkah selanjutnya adalah membuat mereka menjelaskan kepada Anda bagaimana
i[0]
hubungannya&i
. Jika mereka bisa melakukan itu, mereka tidak akan melupakannya dan Anda dapat mulai berbicara tentang struct, bahkan sedikit di muka, hanya saja begitu meresap.Rekomendasi di atas tentang kotak dan panah juga baik, tetapi juga dapat berakhir menyimpang ke dalam diskusi penuh tentang bagaimana memori bekerja - yang merupakan pembicaraan yang harus terjadi di beberapa titik, tetapi dapat mengalihkan perhatian dari titik segera di tangan : bagaimana menafsirkan notasi pointer dalam C.
sumber
int foo = 1;
. Sekarang ini adalah OK:int *bar; *bar = foo;
. Ini tidak apa-apa:int *bar = foo;
Jenis ekspresinya
*bar
adalahint
; dengan demikian, tipe variabel (dan ekspresi)bar
adalahint *
. Karena variabel memiliki tipe pointer, penginisialisasi juga harus memiliki tipe pointer.Ada ketidakkonsistenan antara inisialisasi variabel penunjuk dan penugasan; itu hanya sesuatu yang harus dipelajari dengan cara yang sulit.
sumber
Saya lebih suka membacanya sebagai yang pertama
*
berlaku untukint
lebih daribar
.sumber
int* a, b
tidak melakukan apa yang mereka pikirkan.int* a,b
harus digunakan sama sekali. Untuk kelayakan yang lebih baik, pembaruan, dll ... seharusnya hanya ada satu deklarasi variabel per baris dan tidak pernah lagi. Ini sesuatu untuk dijelaskan kepada pemula juga, bahkan jika kompiler dapat menanganinya.*
sebagai bagian dari tipenya, dan hanya mencegahint* a, b
. Kecuali jika Anda lebih suka mengatakan bahwa itu*a
adalah tipeint
daripadaa
pointer keint
...int *a, b;
tidak boleh digunakan. Mendeklarasikan dua variabel dengan tipe berbeda dalam pernyataan yang sama merupakan praktik yang sangat buruk dan merupakan kandidat yang kuat untuk masalah pemeliharaan. Mungkin berbeda bagi kita yang bekerja di bidang tertanam di mana anint*
dan anint
sering berbeda ukuran dan kadang-kadang disimpan di lokasi memori yang sama sekali berbeda. Ini adalah salah satu dari banyak aspek bahasa C yang paling baik diajarkan karena 'diizinkan, tetapi jangan lakukan itu.'Question 1
: Apa itubar
?Ans
: Ini adalah variabel pointer (untuk mengetikint
). Pointer harus menunjuk ke beberapa lokasi memori yang valid dan kemudian harus direferensikan (* bar) menggunakan operator unary*
untuk membaca nilai yang disimpan di lokasi itu.Question 2
: Apa itu&foo
?Ans
: foo adalah variabel bertipeint
.yang disimpan di beberapa lokasi memori yang valid dan lokasi itu kita dapatkan dari operator&
jadi sekarang yang kita miliki adalah beberapa lokasi memori yang valid&foo
.Jadi keduanya disatukan yaitu apa yang dibutuhkan pointer adalah lokasi memori yang valid dan yang didapat
&foo
sehingga inisialisasi yang baik.Sekarang pointer
bar
menunjuk ke lokasi memori yang valid dan nilai yang tersimpan di dalamnya bisa didapat yaitu dereferencing yaitu*bar
sumber
Anda harus menunjukkan seorang pemula yang * memiliki arti berbeda dalam deklarasi dan ungkapan. Seperti yang Anda ketahui, * dalam ekspresi adalah operator unary, dan * Dalam deklarasi bukan operator dan hanya semacam sintaks yang menggabungkan dengan tipe untuk membuat kompiler tahu bahwa itu adalah tipe pointer. lebih baik mengatakan seorang pemula, "* memiliki arti yang berbeda. Untuk memahami makna *, Anda harus menemukan di mana * digunakan"
sumber
Saya pikir iblis ada di ruang angkasa.
Saya akan menulis (tidak hanya untuk pemula, tetapi untuk diri saya juga): int * bar = & foo; bukannya int * bar = & foo;
ini harus membuktikan apa hubungan antara sintaks dan semantik
sumber
Sudah dicatat bahwa * memiliki banyak peran.
Ada ide sederhana lain yang dapat membantu pemula untuk memahami hal-hal:
Pikirkan bahwa "=" memiliki banyak peran juga.
Ketika penugasan digunakan pada baris yang sama dengan deklarasi, anggap itu sebagai panggilan konstruktor, bukan penugasan sewenang-wenang.
Ketika kamu melihat:
Pikirkan bahwa itu hampir setara dengan:
Tanda kurung lebih diutamakan daripada tanda bintang, jadi "& foo" lebih mudah secara intuitif dikaitkan dengan "bilah" daripada "* bilah".
sumber
Saya melihat pertanyaan ini beberapa hari yang lalu, dan kemudian kebetulan membaca penjelasan deklarasi tipe Go di Go Blog . Ini dimulai dengan memberikan akun deklarasi tipe C, yang sepertinya merupakan sumber yang berguna untuk ditambahkan ke utas ini, meskipun saya pikir ada jawaban yang lebih lengkap yang sudah diberikan.
(Selanjutnya menjelaskan bagaimana memperluas pemahaman ini ke pointer fungsi dll)
Ini adalah cara yang belum pernah saya pikirkan sebelumnya, tetapi sepertinya cara yang cukup mudah untuk menghitung kelebihan sintaks.
sumber
Jika masalahnya adalah sintaks, mungkin bermanfaat untuk menunjukkan kode yang setara dengan templat / using.
Ini kemudian dapat digunakan sebagai
Setelah itu, bandingkan sintaks normal / C dengan pendekatan C ++ ini saja. Ini juga berguna untuk menjelaskan pointer const.
sumber
Sumber kebingungan muncul dari fakta bahwa
*
simbol dapat memiliki makna yang berbeda dalam C, tergantung pada fakta di mana simbol digunakan. Untuk menjelaskan pointer ke pemula, makna*
simbol dalam konteks yang berbeda harus dijelaskan.Dalam deklarasi
yang
*
simbol adalah bukan operator tipuan . Sebagai gantinya, ini membantu untuk menentukan jenisbar
menginformasikan kompiler yangbar
merupakan pointer keint
. Di sisi lain, ketika muncul dalam pernyataan*
simbol (bila digunakan sebagai operator unary ) melakukan tipuan. Karena itu, pernyataannyaakan salah karena memberikan alamat
foo
ke objek yangbar
menunjuk, bukan kebar
dirinya sendiri.sumber
"Mungkin menulisnya sebagai bilah int * membuatnya lebih jelas bahwa bintang itu sebenarnya bagian dari tipe, bukan bagian dari pengenal." Jadi saya lakukan. Dan saya katakan, itu agak mirip dengan Type, tetapi hanya untuk satu nama pointer.
"Tentu saja ini membuatmu mengalami masalah yang berbeda dengan hal-hal yang tidak intuitif seperti int * a, b."
sumber
Di sini Anda harus menggunakan, memahami dan menjelaskan logika kompiler, bukan logika manusia (saya tahu, Anda adalah manusia, tetapi di sini Anda harus meniru komputer ...).
Ketika Anda menulis
kompiler mengelompokkan itu sebagai
Yaitu: di sini adalah variabel baru, namanya
bar
, tipenya adalah pointer ke int, dan nilai awalnya adalah&foo
.Dan Anda harus menambahkan: yang
=
Menandakan di atas sebuah inisialisasi bukan kepura-puraan, sedangkan di ungkapan berikut*bar = 2;
ini adalah afirmasiEdit per komentar:
Hati-hati: dalam hal beberapa deklarasi
*
hanya terkait dengan variabel berikut:bilah adalah pointer ke int yang diinisialisasi dengan alamat foo, b adalah int yang diinisialisasi ke 2, dan dalam
bilah di penunjuk diam ke int, dan p adalah penunjuk ke penunjuk ke int yang diinisialisasi ke alamat atau bilah.
sumber
int* a, b;
menyatakan a untuk menjadi pointer keint
, tetapi b menjadiint
. The*
simbol hanya memiliki dua makna yang berbeda: Dalam deklarasi, itu menunjukkan jenis pointer, dan ekspresi itu adalah operator dereference unary.*
in rattached ke tipe, sehingga pointer diinisialisasi sedangkan dalam suatu afeksi nilai rujukan dipengaruhi. Tapi setidaknya Anda memberi saya topi yang bagus :-)Pointer pada dasarnya bukan indikasi array. Pemula dengan mudah berpikir bahwa pointer terlihat seperti array. sebagian besar contoh string menggunakan
"char * pstr" bentuknya mirip
"char str [80]"
Tapi, hal-hal penting, Pointer diperlakukan hanya sebagai integer di level bawah dari kompiler.
Mari kita lihat contoh ::
Hasil akan menyukai 0x2a6b7ed0 ini adalah alamat str []
Jadi, pada dasarnya, perlu diingat Pointer adalah semacam Integer. menyajikan Alamat.
sumber
Saya akan menjelaskan bahwa int adalah objek, seperti mengapung dll. Sebuah pointer adalah jenis objek yang nilainya mewakili alamat dalam memori (karenanya mengapa pointer default ke NULL).
Saat pertama kali mendeklarasikan pointer, Anda menggunakan sintaks type-pointer-name. Itu dibaca sebagai "integer-pointer bernama nama yang dapat menunjuk ke alamat objek integer". Kami hanya menggunakan sintaks ini selama deklerasi, mirip dengan bagaimana kami mendeklarasikan int sebagai 'int num1' tetapi kami hanya menggunakan 'num1' ketika kami ingin menggunakan variabel itu, bukan 'int num1'.
int x = 5; // objek integer dengan nilai 5
int * ptr; // bilangan bulat dengan nilai NULL secara default
Untuk membuat titik penunjuk ke alamat suatu objek, kami menggunakan simbol '&' yang dapat dibaca sebagai "alamat".
ptr = & x; // sekarang nilai adalah alamat 'x'
Karena penunjuk hanya alamat dari objek, untuk mendapatkan nilai aktual yang dipegang di alamat itu, kita harus menggunakan simbol '*' yang bila digunakan sebelum penunjuk berarti "nilai pada alamat yang ditunjuk oleh".
std :: cout << * ptr; // cetak nilai di alamat
Anda dapat menjelaskan secara singkat bahwa ' ' adalah 'operator' yang mengembalikan hasil berbeda dengan berbagai jenis objek. Saat digunakan dengan pointer, operator ' ' tidak berarti "dikalikan dengan" lagi.
Ini membantu untuk menggambar diagram yang menunjukkan bagaimana variabel memiliki nama dan nilai dan pointer memiliki alamat (nama) dan nilai dan menunjukkan bahwa nilai pointer akan menjadi alamat int.
sumber
Pointer hanyalah variabel yang digunakan untuk menyimpan alamat.
Memori dalam komputer terdiri dari byte (byte terdiri dari 8 bit) yang disusun secara berurutan. Setiap byte memiliki nomor yang terkait dengannya seperti indeks atau subskrip dalam array, yang disebut alamat byte. Alamat byte dimulai dari 0 hingga kurang dari ukuran memori. Misalnya, katakan dalam RAM 64MB, ada 64 * 2 ^ 20 = 67108864 byte. Oleh karena itu alamat byte ini akan mulai dari 0 hingga 67108863.
Mari kita lihat apa yang terjadi ketika Anda mendeklarasikan variabel.
tanda int;
Seperti yang kita ketahui bahwa suatu int menempati 4 byte data (dengan asumsi kita menggunakan kompiler 32-bit), maka kompiler menyimpan 4 byte berturut-turut dari memori untuk menyimpan nilai integer. Alamat byte pertama dari 4 byte yang dialokasikan dikenal sebagai alamat dari tanda variabel. Katakanlah alamat 4 byte berturut-turut adalah 5004, 5005, 5006 dan 5007 maka alamat dari tanda variabel adalah 5004.
Mendeklarasikan variabel pointer
Seperti yang sudah dikatakan pointer adalah variabel yang menyimpan alamat memori. Sama seperti variabel lain yang Anda perlu mendeklarasikan variabel pointer terlebih dahulu sebelum Anda dapat menggunakannya. Inilah cara Anda dapat mendeklarasikan variabel pointer.
Sintaksis:
data_type *pointer_name;
data_type adalah tipe dari pointer (juga dikenal sebagai tipe dasar dari pointer). pointer_name adalah nama variabel, yang bisa berupa pengidentifikasi C apa pun yang valid.
Mari kita ambil beberapa contoh:
int * ip berarti bahwa ip adalah variabel penunjuk yang mampu menunjuk ke variabel tipe int. Dengan kata lain, variabel pointer ip dapat menyimpan alamat variabel tipe saja. Demikian pula, variabel pointer fp hanya dapat menyimpan alamat variabel tipe float. Tipe variabel (juga dikenal sebagai tipe dasar) ip adalah pointer ke int dan tipe fp adalah pointer ke float. Variabel pointer tipe pointer ke int dapat secara simbolis direpresentasikan sebagai (int *). Demikian pula, variabel pointer tipe pointer ke float dapat direpresentasikan sebagai (float *)
Setelah mendeklarasikan variabel pointer, langkah selanjutnya adalah menetapkan beberapa alamat memori yang valid untuknya. Anda seharusnya tidak pernah menggunakan variabel pointer tanpa menetapkan beberapa alamat memori yang valid untuk itu, karena tepat setelah deklarasi itu berisi nilai sampah dan mungkin menunjuk ke mana saja di memori. Penggunaan pointer yang tidak ditetapkan dapat memberikan hasil yang tidak terduga. Bahkan dapat menyebabkan program macet.
Sumber: thecguru sejauh ini merupakan penjelasan paling sederhana namun terperinci yang pernah saya temukan.
sumber