Dalam sumber terpercaya tentang C, informasi berikut diberikan setelah mendiskusikan &
operator:
... Agak disayangkan bahwa terminologi [alamat] tetap ada, karena membingungkan mereka yang tidak tahu alamat apa itu, dan menyesatkan mereka yang melakukannya: memikirkan pointer seolah-olah alamat itu biasanya mengarah pada kesedihan .. .
Bahan-bahan lain yang saya baca (dari sumber yang memiliki reputasi sama, saya katakan) selalu tanpa malu-malu merujuk pointer dan &
operator sebagai memberikan alamat memori. Saya akan senang untuk terus mencari aktualitas dari masalah ini, tetapi agak sulit ketika sumber-sumber yang bereputasi JENIS tidak setuju.
Sekarang saya sedikit bingung - apa sebenarnya pointer itu, lalu, kalau bukan alamat memori?
PS
Penulis kemudian mengatakan: ... Saya akan terus menggunakan istilah 'alamat', karena untuk menciptakan [istilah] yang berbeda akan lebih buruk.
sumber
Jawaban:
Standar C tidak mendefinisikan apa pointer secara internal dan bagaimana kerjanya secara internal. Ini disengaja agar tidak membatasi jumlah platform, di mana C dapat diimplementasikan sebagai bahasa yang dikompilasi atau ditafsirkan.
Nilai pointer dapat berupa semacam ID atau pegangan atau kombinasi beberapa ID (say hello to x86 segment and offsets) dan tidak harus alamat memori yang sebenarnya. ID ini bisa berupa apa saja, bahkan string teks berukuran tetap. Representasi non-alamat mungkin sangat berguna untuk juru bahasa C.
sumber
Saya tidak yakin tentang sumber Anda, tetapi jenis bahasa yang Anda gambarkan berasal dari standar C:
Jadi ... ya, petunjuk menunjuk ke alamat memori. Setidaknya begitulah arti standar C.
Untuk mengatakannya sedikit lebih jelas, pointer adalah variabel yang menyimpan nilai beberapa alamat . Alamat objek (yang dapat disimpan dalam pointer) dikembalikan dengan
&
operator unary .Saya dapat menyimpan alamat "42 Wallaby Way, Sydney" dalam sebuah variabel (dan variabel itu akan menjadi semacam "pointer", tetapi karena itu bukan alamat memori, itu bukan sesuatu yang kita sebut "pointer" dengan benar). Komputer Anda memiliki alamat untuk ember memorinya. Pointer menyimpan nilai alamat (yaitu pointer menyimpan nilai "42 Wallaby Way, Sydney", yang merupakan alamat).
Sunting: Saya ingin memperluas komentar Alexey Frunze.
Apa itu pointer? Mari kita lihat standar C:
Pada dasarnya, pointer menyimpan nilai yang menyediakan referensi ke beberapa objek atau fungsi. Agak. Pointer dimaksudkan untuk menyimpan nilai yang menyediakan referensi ke beberapa objek atau fungsi, tetapi itu tidak selalu terjadi:
Kutipan di atas mengatakan bahwa kita dapat mengubah integer menjadi sebuah pointer. Jika kita melakukan itu (yaitu, jika kita memasukkan nilai integer ke dalam pointer alih-alih referensi tertentu ke objek atau fungsi), maka pointer "mungkin tidak menunjuk ke entitas tipe referensi" (yaitu mungkin tidak memberikan referensi ke suatu objek atau fungsi). Mungkin memberi kita sesuatu yang lain. Dan ini adalah satu tempat di mana Anda mungkin menempelkan semacam pegangan atau ID dalam sebuah pointer (yaitu pointer tidak menunjuk ke suatu objek; itu menyimpan nilai yang mewakili sesuatu, tetapi nilai itu mungkin bukan alamat).
Jadi ya, seperti yang dikatakan Alexey Frunze, mungkin saja pointer tidak menyimpan alamat ke objek atau fungsi. Mungkin saja sebuah pointer malah menyimpan semacam "handle" atau ID, dan Anda dapat melakukan ini dengan menetapkan beberapa nilai integer yang berubah-ubah menjadi sebuah pointer. Apa yang menangani atau ID ini wakili tergantung pada sistem / lingkungan / konteks. Selama sistem / implementasi Anda dapat memahami nilainya, Anda dalam kondisi yang baik (tetapi itu tergantung pada nilai spesifik dan sistem / implementasi tertentu).
Biasanya , pointer menyimpan alamat ke objek atau fungsi. Jika tidak menyimpan alamat aktual (ke objek atau fungsi), hasilnya adalah implementasi yang ditentukan (artinya apa yang sebenarnya terjadi dan apa yang ditunjukkan oleh pointer tergantung pada sistem dan implementasi Anda, jadi itu mungkin merupakan pegangan atau ID pada sistem tertentu, tetapi menggunakan kode / nilai yang sama pada sistem lain mungkin merusak program Anda).
Itu akhirnya menjadi lebih lama dari yang saya kira akan ...
sumber
Dalam gambar ini,
pointer_p adalah pointer yang terletak di 0x12345, dan menunjuk ke variabel variable_v di 0x34567.
sumber
Memikirkan pointer sebagai alamat adalah perkiraan . Seperti semua perkiraan, terkadang cukup baik untuk berguna, tetapi juga tidak tepat yang berarti bahwa mengandalkannya menyebabkan masalah.
Pointer seperti alamat yang menunjukkan di mana menemukan objek. Satu batasan langsung dari analogi ini adalah bahwa tidak semua petunjuk benar-benar berisi alamat.
NULL
adalah pointer yang bukan alamat. Isi dari variabel pointer sebenarnya bisa dari satu dari tiga jenis:p
berisi alamatx
maka ekspresi*p
memiliki nilai yang sama sepertix
);NULL
merupakan contoh;p
tidak memiliki nilai yang valid, maka*p
dapat melakukan apa saja ("perilaku tidak terdefinisi"), dengan menabrakkan program kemungkinan yang cukup umum).Selain itu, akan lebih akurat untuk mengatakan bahwa sebuah pointer (jika valid dan non-null) berisi alamat: sebuah pointer menunjukkan di mana menemukan sebuah objek, tetapi ada lebih banyak informasi yang terikat padanya.
Secara khusus, pointer memiliki tipe. Pada sebagian besar platform, tipe pointer tidak memiliki pengaruh saat runtime, tetapi memiliki pengaruh yang melampaui tipe pada waktu kompilasi. Jika
p
adalah pointer keint
(int *p;
), makap + 1
menunjuk ke integer yangsizeof(int)
byte setelahp
(dengan asumsip + 1
masih pointer yang valid). Jikaq
adalah pointer kechar
yang menunjuk ke alamat yang sama denganp
(char *q = p;
), makaq + 1
bukan alamat yang sama denganp + 1
. Jika Anda menganggap pointer sebagai alamat, itu tidak terlalu intuitif bahwa "alamat berikutnya" berbeda untuk pointer yang berbeda ke lokasi yang sama.Dimungkinkan dalam beberapa lingkungan untuk memiliki beberapa nilai penunjuk dengan representasi berbeda (pola bit berbeda dalam memori) yang menunjuk ke lokasi yang sama dalam memori. Anda dapat menganggap ini sebagai pointer yang berbeda yang memegang alamat yang sama, atau sebagai alamat yang berbeda untuk lokasi yang sama - metafornya tidak jelas dalam kasus ini. The
==
Operator selalu memberitahu Anda apakah dua operan yang menunjuk ke lokasi yang sama, sehingga pada lingkungan ini Anda dapat memilikip == q
meskipunp
danq
memiliki pola bit yang berbeda.Bahkan ada lingkungan di mana pointer membawa informasi lain di luar alamat, seperti jenis atau informasi izin. Anda dapat dengan mudah menjalani hidup Anda sebagai seorang programmer tanpa menemui ini.
Ada lingkungan di mana berbagai jenis pointer memiliki representasi yang berbeda. Anda dapat menganggapnya sebagai berbagai jenis alamat yang memiliki representasi berbeda. Misalnya, beberapa arsitektur memiliki pointer byte dan pointer kata, atau pointer objek dan pointer fungsi.
Secara keseluruhan, memikirkan pointer sebagai alamat tidak terlalu buruk selama Anda mengingatnya
Sebaliknya, jauh lebih merepotkan. Tidak semua yang tampak seperti alamat bisa menjadi pointer . Di suatu tempat jauh di bawah pointer apa pun direpresentasikan sebagai pola bit yang dapat dibaca sebagai integer, dan Anda dapat mengatakan bahwa integer ini adalah alamat. Namun sebaliknya, tidak setiap integer adalah sebuah pointer.
Ada beberapa keterbatasan pertama yang diketahui; misalnya, bilangan bulat yang menunjuk lokasi di luar ruang alamat program Anda tidak bisa menjadi penunjuk yang valid. Alamat yang tidak selaras tidak membuat penunjuk yang valid untuk tipe data yang membutuhkan perataan; misalnya, pada platform di mana
int
memerlukan perataan 4-byte, 0x7654321 tidak dapat menjadiint*
nilai yang valid .Namun, lebih dari itu, karena ketika Anda membuat pointer menjadi bilangan bulat, Anda berada dalam dunia yang penuh masalah. Sebagian besar dari masalah ini adalah bahwa mengoptimalkan kompiler jauh lebih baik dalam optimasi mikro daripada kebanyakan programmer, sehingga model mental mereka tentang bagaimana suatu program bekerja sangat salah. Hanya karena Anda memiliki pointer dengan alamat yang sama tidak berarti bahwa mereka setara. Misalnya, pertimbangkan cuplikan berikut:
Anda mungkin mengharapkan itu pada mesin run-of-the-mill di mana
sizeof(int)==4
dansizeof(short)==2
, ini bisa mencetak1 = 1?
(little-endian) atau65536 = 1?
(big-endian). Tetapi pada PC Linux 64-bit saya dengan GCC 4.4:GCC cukup berbaik hati untuk memperingatkan kita apa yang salah dalam contoh sederhana ini - dalam contoh yang lebih kompleks, kompiler mungkin tidak memperhatikan. Karena
p
memiliki tipe yang berbeda&x
, mengubahp
poin apa yang tidak dapat memengaruhi&x
poin apa (di luar beberapa pengecualian yang ditentukan dengan baik). Oleh karena itu kompiler bebas untuk menyimpan nilaix
dalam register dan tidak memperbarui register ini sebagai*p
perubahan. Program ini merujuk dua pointer ke alamat yang sama dan mendapatkan dua nilai yang berbeda!Moral dari contoh ini adalah bahwa memikirkan pointer (non-null valid) sebagai alamat baik-baik saja, selama Anda tetap berada dalam aturan yang tepat dari bahasa C. Sisi lain dari koin adalah bahwa aturan bahasa C rumit, dan sulit untuk mendapatkan perasaan intuitif kecuali Anda tahu apa yang terjadi di bawah tenda. Dan apa yang terjadi di balik tudung adalah bahwa ikatan antara pointer dan alamat agak longgar, baik untuk mendukung arsitektur prosesor "eksotis" dan untuk mendukung optimalisasi kompiler.
Jadi pikirkan pointer sebagai alamat sebagai langkah pertama dalam pemahaman Anda, tetapi jangan ikuti intuisi itu terlalu jauh.
sumber
*p = 3
dijamin berhasil ketika p belum diinisialisasi.NULL
tidak, tetapi untuk tingkat detail yang diperlukan di sini, ini adalah gangguan yang tidak relevan. Bahkan untuk pemrograman sehari-hari, fakta yangNULL
dapat diimplementasikan sebagai sesuatu yang tidak mengatakan "pointer" tidak sering muncul (terutama beralihNULL
ke fungsi variadik - tetapi bahkan di sana, jika Anda tidak melemparkannya , Anda sudah membuat asumsi bahwa semua tipe pointer memiliki representasi yang sama).Pointer adalah variabel yang HOLDS alamat memori, bukan alamat itu sendiri. Namun, Anda dapat melakukan dereferensi pointer - dan mendapatkan akses ke lokasi memori.
Sebagai contoh:
Itu dia. Sesederhana itu.
Sebuah program untuk menunjukkan apa yang saya katakan dan hasilnya ada di sini:
http://ideone.com/rcSUsb
Program:
sumber
fopen
ke dalam variabel jika Anda perlu menggunakannya lebih dari sekali (yang, untukfopen
, hampir sepanjang waktu).Sulit untuk mengatakan dengan tepat apa arti sebenarnya dari para penulis buku itu. Apakah pointer berisi alamat atau tidak tergantung pada bagaimana Anda menentukan alamat dan bagaimana Anda menentukan sebuah pointer.
Menilai dari semua jawaban yang ditulis, beberapa orang berasumsi bahwa (1) alamat harus bilangan bulat dan (2) sebuah pointer tidak perlu secara virtual tidak dikatakan demikian dalam spesifikasi. Dengan asumsi ini, maka petunjuk jelas tidak harus berisi alamat.
Namun, kita melihat bahwa sementara (2) mungkin benar, (1) mungkin tidak harus benar. Dan apa yang membuat fakta bahwa & disebut alamat operator sesuai jawaban @ CornStalks? Apakah ini berarti bahwa penulis spesifikasi bermaksud untuk pointer berisi alamat?
Jadi bisakah kita mengatakan, pointer berisi alamat, tetapi alamat tidak harus berupa bilangan bulat? Mungkin.
Saya pikir semua ini adalah omong kosong semantik pembicaraan semantik. Sama sekali tidak ada artinya secara praktis. Bisakah Anda memikirkan kompiler yang menghasilkan kode sedemikian rupa sehingga nilai pointer bukan alamat? Jika ya, apa? Itulah yang saya pikir...
Saya pikir apa yang penulis buku ini (kutipan pertama yang mengklaim bahwa pointer tidak harus hanya alamat) mungkin merujuk pada fakta bahwa pointer disertai dengan informasi tipe yang melekat.
Sebagai contoh,
keduanya y dan z adalah pointer, tetapi y +1 dan z +1 berbeda. jika itu adalah alamat memori, bukankah ekspresi itu memberi Anda nilai yang sama?
Dan di sinilah letak pemikiran tentang petunjuk seolah-olah alamat itu biasanya mengarah ke kesedihan . Bug telah ditulis karena orang berpikir tentang pointer seolah-olah alamat , dan ini biasanya mengarah ke kesedihan .
55555 mungkin bukan pointer, meskipun mungkin alamat, tetapi (int *) 55555 adalah pointer. 55555 + 1 = 55556, tetapi (int *) 55555 + 1 adalah 55559 (+/- perbedaan dalam hal sizeof (int)).
sumber
far
pointer bukan hanya "integer".Nah, pointer adalah abstraksi yang mewakili lokasi memori. Perhatikan bahwa kutipan tidak mengatakan bahwa memikirkan pointer seolah-olah itu adalah alamat memori salah, itu hanya mengatakan bahwa "biasanya mengarah ke kesedihan". Dengan kata lain, itu membuat Anda memiliki harapan yang salah.
Sumber kesedihan yang paling mungkin tentu saja adalah aritmatika penunjuk, yang sebenarnya merupakan salah satu kekuatan C. Jika pointer adalah alamat, Anda akan mengharapkan aritmatika pointer menjadi aritmatika alamat; tapi ternyata tidak. Misalnya, menambahkan 10 ke alamat harus memberi Anda alamat yang lebih besar dengan 10 unit pengalamatan; tetapi menambahkan 10 ke sebuah pointer menambahnya dengan 10 kali ukuran dari jenis objek yang ditunjuknya (dan bahkan bukan ukuran sebenarnya, tetapi dibulatkan ke batas penyelarasan). Dengan
int *
arsitektur biasa dengan bilangan bulat 32-bit, menambahkan 10 ke dalamnya akan menambahnya dengan 40 unit pengalamatan (byte). Pemrogram C yang berpengalaman mengetahui hal ini dan hidup dengannya, tetapi penulis Anda jelas bukan penggemar metafora yang ceroboh.Ada pertanyaan tambahan tentang bagaimana isi pointer menunjukkan lokasi memori: Seperti banyak jawaban yang telah dijelaskan, alamat tidak selalu berupa int (atau panjang). Dalam beberapa arsitektur alamat adalah "segmen" plus offset. Sebuah pointer bahkan mungkin hanya berisi offset ke segmen saat ini ("near" pointer), yang dengan sendirinya bukan alamat memori yang unik. Dan isi pointer mungkin hanya memiliki hubungan tidak langsung ke alamat memori sebagai perangkat keras memahaminya. Tetapi penulis kutipan yang dikutip bahkan tidak menyebutkan representasi, jadi saya pikir itu adalah kesetaraan konseptual, bukan representasi, yang ada dalam pikiran mereka.
sumber
Inilah cara saya menjelaskannya kepada beberapa orang yang bingung di masa lalu: Penunjuk memiliki dua atribut yang memengaruhi perilakunya. Ini memiliki nilai , yang (dalam lingkungan khas) alamat memori, dan tipe , yang memberi tahu Anda jenis dan ukuran objek yang ditunjuknya.
Misalnya, diberikan:
Anda dapat memiliki tiga petunjuk berbeda yang menunjuk ke objek yang sama:
Jika Anda membandingkan nilai pointer ini, semuanya sama:
Namun, jika Anda menambah setiap pointer, Anda akan melihat bahwa tipe yang mereka tunjuk menjadi relevan.
Variabel
i
danc
akan memiliki nilai yang berbeda pada titik ini, karenai++
menyebabkani
mengandung alamat integer yang dapat diakses berikutnya, danc++
menyebabkanc
menunjuk ke karakter berikutnya yang dapat dialamatkan. Biasanya, bilangan bulat mengambil lebih banyak memori daripada karakter, sehinggai
akan berakhir dengan nilai yang lebih besar daripadac
setelah keduanya bertambah.sumber
i == c
tidak berbentuk (Anda hanya dapat membandingkan pointer ke tipe yang berbeda jika ada konversi implisit dari satu ke yang lain). Selanjutnya, memperbaiki ini dengan gips berarti Anda telah menerapkan konversi, dan kemudian dapat diperdebatkan apakah konversi itu mengubah nilainya atau tidak. (Anda dapat menyatakan bahwa itu tidak benar, tetapi kemudian itu hanya menegaskan hal yang sama yang Anda coba buktikan dengan contoh ini).Mark Bessey sudah mengatakannya, tetapi ini perlu ditekankan kembali hingga dipahami.
Pointer lebih banyak berkaitan dengan variabel daripada literal 3.
Pointer adalah tuple dari nilai (alamat) dan tipe (dengan properti tambahan, seperti hanya baca). Jenis (dan parameter tambahan jika ada) dapat lebih jauh menentukan atau membatasi konteks; misalnya.
__far ptr, __near ptr
: apa konteks dari alamat: stack, heap, alamat linear, offset dari suatu tempat, memori fisik atau apa.Ini adalah properti tipe yang membuat pointer aritmatika sedikit berbeda dengan integer aritmatika.
Contoh counter dari pointer yang tidak menjadi variabel terlalu banyak untuk diabaikan
fopen mengembalikan pointer FILE. (dimana variabelnya)
tumpukan penunjuk atau penunjuk bingkai adalah register yang biasanya tidak dapat disentuh
*(int *)0x1231330 = 13;
- casting nilai integer sembarang ke tipe pointer_of_integer dan menulis / membaca integer tanpa pernah memperkenalkan variabelDalam masa program C akan ada banyak contoh pointer sementara yang tidak memiliki alamat - dan karena itu mereka bukan variabel, tetapi ekspresi / nilai dengan tipe waktu kompilasi terkait.
sumber
Anda benar dan waras. Biasanya, sebuah pointer hanyalah sebuah alamat, sehingga Anda dapat melemparkannya ke integer dan melakukan aritmatika apa saja.
Tetapi kadang-kadang pointer hanya bagian dari alamat. Pada beberapa arsitektur, pointer dikonversi ke alamat dengan penambahan basis atau register CPU lain digunakan.
Tapi hari ini, pada arsitektur PC dan ARM dengan model memori datar dan bahasa C yang dikompilasi secara native, tidak masalah untuk berpikir bahwa pointer adalah alamat integer ke suatu tempat dalam RAM satu dimensi yang dapat dialamatkan.
sumber
Pointer, seperti variabel lain dalam C, pada dasarnya adalah kumpulan bit yang dapat diwakili oleh satu atau lebih
unsigned char
nilai-nilai yang disatukan (seperti dengan jenis cariable lainnya,sizeof(some_variable)
akan menunjukkan jumlahunsigned char
nilai). Apa yang membuat pointer berbeda dari variabel lain adalah bahwa kompiler C akan menafsirkan bit dalam pointer sebagai mengidentifikasi, entah bagaimana, tempat di mana variabel dapat disimpan. Dalam C, tidak seperti beberapa bahasa lain, dimungkinkan untuk meminta ruang untuk beberapa variabel, dan kemudian mengonversi pointer ke nilai apa pun di set ke pointer ke variabel lain di dalam set itu.Banyak kompiler mengimplementasikan pointer dengan menggunakan bit mereka menyimpan alamat mesin yang sebenarnya, tetapi itu bukan satu-satunya implementasi yang mungkin. Implementasi dapat membuat satu array - tidak dapat diakses oleh kode pengguna - daftar alamat perangkat keras dan ukuran yang dialokasikan dari semua objek memori (set variabel) yang digunakan program, dan masing-masing pointer berisi indeks ke dalam array bersama dengan offset dari indeks itu. Desain seperti itu akan memungkinkan suatu sistem tidak hanya membatasi kode untuk hanya beroperasi pada memori yang dimilikinya, tetapi juga memastikan bahwa penunjuk ke satu item memori tidak dapat secara tidak sengaja dikonversi menjadi penunjuk ke item memori lain (dalam sistem yang menggunakan perangkat keras alamat, jika
foo
danbar
adalah array dari 10 item yang disimpan secara berurutan dalam memori, sebuah penunjuk ke item "kesebelas" darifoo
mungkin malah menunjuk ke item pertamabar
, tetapi dalam sistem di mana setiap "pointer" adalah ID objek dan offset, sistem dapat menjebak jika kode mencoba mengindeks pointer kefoo
luar kisaran yang dialokasikan). Mungkin juga bagi sistem semacam itu untuk menghilangkan masalah fragmentasi memori, karena alamat fisik yang terkait dengan petunjuk apa pun dapat dipindahkan.Perhatikan bahwa sementara pointer agak abstrak, mereka tidak cukup abstrak untuk memungkinkan kompiler C yang sepenuhnya memenuhi standar untuk mengimplementasikan pengumpul sampah. Kompiler C menentukan bahwa setiap variabel, termasuk pointer, direpresentasikan sebagai urutan
unsigned char
nilai. Diberikan variabel apa pun, seseorang dapat menguraikannya menjadi urutan angka dan kemudian mengubah urutan angka itu kembali menjadi variabel dari tipe aslinya. Akibatnya, akan mungkin bagi suatu program untuk melakukannyacalloc
beberapa penyimpanan (menerima pointer ke sana), menyimpan sesuatu di sana, menguraikan pointer menjadi serangkaian byte, menampilkannya di layar, dan kemudian menghapus semua referensi ke sana. Jika program kemudian menerima beberapa angka dari keyboard, merekonstitusi mereka menjadi sebuah pointer, dan kemudian mencoba membaca data dari pointer itu, dan jika pengguna memasukkan angka yang sama dengan yang sebelumnya ditampilkan oleh program, program tersebut akan diminta untuk menampilkan data yang telah disimpan dalamcalloc
memori ed. Karena tidak ada cara yang memungkinkan komputer dapat mengetahui apakah pengguna telah membuat salinan angka-angka yang ditampilkan, tidak akan ada kemungkinan komputer dapat mengetahui apakah memori yang disebutkan di atas mungkin dapat diakses di masa depan.sumber
free
disebut secara eksplisit, tentu saja). Apakah implementasi yang dihasilkan akan sangat bermanfaat adalah masalah lain, karena kemampuannya untuk mengumpulkan mungkin terlalu terbatas, tetapi Anda setidaknya bisa menyebutnya sebagai pemulung :-) Penunjukan pointer dan aritmatika tidak akan "membocorkan" nilainya, tetapi setiap akses ke sumber yangchar*
tidak diketahui asalnya harus diperiksa.free
belum dipanggil, atau mencegah referensi apa pun ke objek yang dibebaskan agar tidak menjadi referensi ke objek langsung [bahkan ketika menggunakan sumber daya yang membutuhkan manajemen seumur hidup eksplisit, GC masih bisa berguna melakukan fungsi yang terakhir]; sistem GC yang kadang-kadang salah menganggap objek memiliki referensi langsung ke mereka dapat digunakan jika probabilitas objek N yang perlu disematkan secara simultan mendekati nol ketika N menjadi besar . Kecuali ada yang mau menandai kesalahan kompiler ...Pointer adalah tipe variabel yang secara native tersedia di C / C ++ dan berisi alamat memori. Seperti variabel lainnya, ia memiliki alamatnya sendiri dan membutuhkan memori (jumlahnya khusus platform).
Satu masalah yang akan Anda lihat sebagai hasil dari kebingungan adalah mencoba mengubah referensi dalam suatu fungsi hanya dengan melewatkan pointer berdasarkan nilai. Ini akan membuat salinan dari pointer pada lingkup fungsi dan setiap perubahan di mana "poin" pointer baru ini tidak akan mengubah referensi dari pointer pada lingkup yang memanggil fungsi. Untuk memodifikasi pointer aktual dalam suatu fungsi, seseorang biasanya meneruskan sebuah pointer ke sebuah pointer.
sumber
RINGKASAN SINGKAT (yang juga akan saya tempatkan di atas):
(0) Memikirkan pointer sebagai alamat seringkali merupakan alat pembelajaran yang baik, dan seringkali implementasi aktual untuk pointer ke tipe data biasa.
(1) Tetapi pada banyak, mungkin sebagian besar, kompiler pointer ke fungsi bukan alamat, tetapi lebih besar dari alamat (biasanya 2x, kadang-kadang lebih), atau sebenarnya pointer ke struct dalam memori daripada berisi alamat fungsi dan hal-hal seperti kolam konstan.
(2) Pointer ke anggota data dan pointer ke metode seringkali lebih aneh.
(3) Kode x86 lawas dengan masalah pointer FAR dan NEAR
(4) Beberapa contoh, terutama IBM AS / 400, dengan "penunjuk gemuk" yang aman.
Saya yakin Anda dapat menemukan lebih banyak.
DETAIL:
UMMPPHHH !!!!! Banyak jawaban sejauh ini merupakan jawaban "programmer weenie" yang cukup tipikal - tetapi bukan compiler weenie atau perangkat keras weenie. Karena saya berpura-pura menjadi perangkat keras weenie, dan sering bekerja dengan alat compiler, izinkan saya memasukkan dua sen saya:
Pada banyak, mungkin sebagian besar, kompiler C, sebuah pointer ke data bertipe
T
, pada kenyataannya, adalah alamatT
.Baik.
Tetapi, bahkan pada banyak kompiler ini, pointer tertentu BUKAN alamat. Anda dapat mengatakan ini dengan melihat
sizeof(ThePointer)
.Sebagai contoh, pointer ke fungsi terkadang jauh lebih besar dari alamat biasa. Atau, mereka mungkin melibatkan tingkat tipuan. Artikel inimemberikan satu deskripsi, yang melibatkan prosesor Intel Itanium, tetapi saya telah melihat yang lain. Biasanya, untuk memanggil fungsi, Anda harus tahu tidak hanya alamat kode fungsi, tetapi juga alamat kumpulan fungsi konstan - wilayah memori tempat konstanta dimuat dengan instruksi beban tunggal, bukan dari kompiler yang harus menghasilkan konstanta 64 bit dari beberapa instruksi Load Immediate dan Shift dan OR. Jadi, daripada satu alamat 64 bit, Anda membutuhkan 2 alamat 64 bit. Beberapa ABI (Application Binary Interfaces) memindahkan ini sekitar 128 bit, sedangkan yang lain menggunakan tingkat tipuan, dengan penunjuk fungsi sebenarnya menjadi alamat deskriptor fungsi yang berisi 2 alamat aktual yang baru saja disebutkan. Mana yang lebih baik? Tergantung pada sudut pandang Anda: kinerja, ukuran kode, dan beberapa masalah kompatibilitas - seringkali kode mengasumsikan bahwa pointer dapat dilemparkan ke panjang atau panjang, tetapi juga dapat mengasumsikan bahwa panjangnya persis 64 bit. Kode semacam itu mungkin tidak sesuai standar, namun demikian pelanggan mungkin menginginkannya berfungsi.
Banyak dari kita memiliki kenangan menyakitkan arsitektur Intel x86 yang lama, dengan POINTER DEKAT dan POINTER JAUH. Untungnya ini hampir punah sekarang, jadi hanya ringkasan cepat: dalam mode real 16 bit, alamat linear yang sebenarnya
Padahal dalam mode terproteksi, mungkin saja
dengan alamat yang dihasilkan diperiksa terhadap batas yang ditentukan di segmen. Beberapa program yang digunakan tidak benar-benar standar C / C ++ deklarasi pointer FAR dan NEAR, tetapi banyak yang hanya mengatakan
*T
--- tetapi ada switch kompilator dan linker jadi, misalnya, kode pointer mungkin dekat pointer, hanya offset 32 bit terhadap apa pun yang ada di register CS (Segmen Kode), sedangkan pointer data mungkin adalah pointer FAR, menentukan nomor segmen 16 bit dan offset 32 bit untuk nilai 48 bit. Sekarang, kedua jumlah ini tentu terkait dengan alamat, tetapi karena mereka tidak ukuran yang sama, yang mana dari mereka adalah alamat? Selain itu, segmen juga membawa izin - baca-saja, baca-tulis, dapat dieksekusi - di samping hal-hal yang terkait dengan alamat aktual.Contoh yang lebih menarik, IMHO, adalah (atau, mungkin, adalah) keluarga IBM AS / 400. Komputer ini adalah salah satu yang pertama menerapkan OS di C ++. Pointer pada machime ini biasanya 2X ukuran alamat yang sebenarnya - misalnya sebagai presentasi inimengatakan, 128 bit pointer, tetapi alamat sebenarnya adalah 48-64 bit, dan, sekali lagi, beberapa info tambahan, apa yang disebut kemampuan, yang memberikan izin seperti baca, tulis, serta batas untuk mencegah buffer overflow. Ya: Anda dapat melakukan ini sesuai dengan C / C ++ - dan jika ini ada di mana-mana, PLA Cina dan mafia slavia tidak akan meretas ke banyak sistem komputer Barat. Namun secara historis sebagian besar pemrograman C / C ++ telah mengabaikan keamanan untuk kinerja. Yang paling menarik, keluarga AS400 memungkinkan sistem operasi untuk membuat pointer aman, yang dapat diberikan pada kode yang tidak terprivasi, tetapi yang tidak bisa dipalsukan atau diubah oleh kode yang tidak terprivasi. Sekali lagi, keamanan, dan meskipun standar memenuhi syarat, banyak kode C / C ++ yang tidak memenuhi standar yang ceroboh tidak akan berfungsi dalam sistem yang aman. Sekali lagi, ada standar resmi,
Sekarang, saya akan keluar dari kotak sabun keamanan saya, dan menyebutkan beberapa cara lain di mana pointer (dari berbagai jenis) sering tidak benar-benar alamat: Pointer ke anggota data, pointer ke metode fungsi anggota, dan versi statisnya lebih besar daripada alamat biasa. Seperti yang dikatakan oleh posting ini :
Karena Anda mungkin dapat menebak dari pontificating saya pada (dalam) keamanan, saya telah terlibat dalam proyek perangkat keras / perangkat lunak C / C ++ di mana pointer diperlakukan lebih seperti kemampuan daripada alamat mentah.
Saya bisa terus, tapi saya harap Anda mendapatkan ide.
RINGKASAN SINGKAT (yang juga akan saya tempatkan di atas):
(0) memikirkan pointer sebagai alamat seringkali merupakan alat pembelajaran yang baik, dan seringkali implementasi aktual untuk pointer ke tipe data biasa.
(1) Tetapi pada banyak, mungkin sebagian besar, kompiler pointer ke fungsi bukan alamat, tetapi lebih besar dari alamat (biasanya 2X, kadang-kadang lebih), atau sebenarnya pointer ke struct di memori daripada berisi alamat fungsi dan hal-hal seperti kolam konstan.
(2) Pointer ke anggota data dan pointer ke metode seringkali lebih aneh.
(3) Kode x86 lawas dengan masalah pointer FAR dan NEAR
(4) Beberapa contoh, terutama IBM AS / 400, dengan "penunjuk gemuk" yang aman.
Saya yakin Anda dapat menemukan lebih banyak.
sumber
LinearAddress = SegmentRegister.Selector * 16 + Offset
(perhatikan waktu 16, tidak bergeser 16). Dalam mode terproteksiLinearAddress = SegmentRegister.base + offset
(tidak ada penggandaan apa pun; basis segmen disimpan di GDT / LDT dan di-cache dalam register segmen apa adanya ).Pointer hanyalah variabel lain yang digunakan untuk menyimpan alamat lokasi memori (biasanya alamat memori variabel lain).
sumber
Anda bisa melihatnya dengan cara ini. Pointer adalah nilai yang mewakili alamat di ruang memori addressable.
sumber
Pointer hanyalah variabel lain yang dapat berisi alamat memori biasanya dari variabel lain. Pointer yang menjadi variabel juga memiliki alamat memori.
sumber
Pointer AC sangat mirip dengan alamat memori tetapi dengan detail yang tergantung pada mesin disarikan, serta beberapa fitur yang tidak ditemukan dalam set instruksi level bawah.
Misalnya, penunjuk C relatif kaya diketik. Jika Anda menambah pointer melalui array struktur, itu baik melompat dari satu struktur ke yang lain.
Pointer tunduk pada aturan konversi dan menyediakan kompilasi pengecekan tipe waktu.
Ada nilai "null pointer" khusus yang portabel di tingkat kode sumber, tetapi perwakilannya mungkin berbeda. Jika Anda menetapkan konstanta integer yang nilainya nol ke sebuah pointer, pointer itu mengambil nilai null pointer. Ditto jika Anda menginisialisasi pointer seperti itu.
Pointer dapat digunakan sebagai variabel boolean: ia menguji true jika itu selain null, dan false jika itu null.
Dalam bahasa mesin, jika null pointer adalah alamat lucu seperti 0xFFFFFFFF, maka Anda mungkin harus memiliki tes eksplisit untuk nilai itu. C menyembunyikan itu darimu. Bahkan jika pointer nol 0xFFFFFFFF, Anda dapat mengujinya menggunakan
if (ptr != 0) { /* not null! */}
.Penggunaan pointer yang menumbangkan tipe sistem mengarah ke perilaku yang tidak terdefinisi, sedangkan kode yang serupa dalam bahasa mesin mungkin didefinisikan dengan baik. Assembler akan mengumpulkan instruksi yang telah Anda tulis, tetapi kompiler C akan mengoptimalkan berdasarkan asumsi bahwa Anda tidak melakukan kesalahan. Jika sebuah
float *p
pointer menunjuk ke suatulong n
variabel, dan*p = 0.0
dieksekusi, kompiler tidak diharuskan untuk menangani ini. Penggunaan selanjutnyan
tidak perlu membaca pola bit dari nilai float, tetapi mungkin, itu akan menjadi akses yang dioptimalkan yang didasarkan pada asumsi "aliasing ketat" yangn
belum tersentuh! Artinya, asumsi bahwa program itu berperilaku baik, dan karenanyap
tidak boleh menunjukn
.Di C, pointer ke kode dan pointer ke data berbeda, tetapi pada banyak arsitektur, alamatnya sama. Kompiler C dapat dikembangkan yang memiliki pointer "gemuk", meskipun arsitektur target tidak. Pointer lemak berarti bahwa pointer tidak hanya alamat mesin, tetapi mengandung info lain, seperti informasi tentang ukuran objek yang sedang diarahkan, untuk memeriksa batas. Program tertulis yang mudah dibawa akan dengan mudah porting ke kompiler tersebut.
Jadi Anda bisa lihat, ada banyak perbedaan semantik antara alamat mesin dan pointer C.
sumber
ptr != 0
yang bukan merupakan tes nol, harap ungkapkan identitasnya (tetapi sebelum Anda melakukannya, kirim laporan bug ke vendor).Sebelum memahami petunjuk, kita perlu memahami objek. Objek adalah entitas yang ada dan memiliki penentu lokasi yang disebut alamat. Pointer adalah hanya variabel seperti variabel lain
C
dengan tipe yang disebutpointer
kontennya ditafsirkan sebagai alamat objek yang mendukung operasi berikut.Pointer diklasifikasikan berdasarkan jenis objek yang saat ini dirujuk. Satu-satunya bagian dari informasi yang penting adalah ukuran objek.
Setiap objek mendukung operasi,
&
(alamat), yang mengambil penentu lokasi (alamat) objek sebagai tipe objek pointer. Ini harus mengurangi kebingungan seputar nomenklatur karena hal ini masuk akal untuk memanggil&
sebagai operasi objek daripada pointer yang tipe hasilnya adalah pointer dari tipe objek.Catatan Sepanjang penjelasan ini, saya telah meninggalkan konsep memori.
sumber
&
sebagai 'Alamat' karena itu lebih terkait dengan Obyek daripada pointer per se`Alamat digunakan untuk mengidentifikasi sepotong penyimpanan ukuran tetap, biasanya untuk setiap byte, sebagai integer. Ini tepatnya disebut sebagai alamat byte , yang juga digunakan oleh ISO C. Mungkin ada beberapa metode lain untuk membangun alamat, misalnya untuk setiap bit. Namun, hanya alamat byte yang sering digunakan, kami biasanya menghilangkan "byte".
Secara teknis, alamat tidak pernah menjadi nilai dalam C, karena definisi istilah "nilai" dalam (ISO) C adalah:
(Ditekankan oleh saya.) Namun, tidak ada "tipe alamat" di C.
Pointer tidak sama. Pointer adalah sejenis tipe dalam bahasa C. Ada beberapa tipe pointer yang berbeda. Mereka tidak selalu taat set identik aturan bahasa, misalnya efek
++
pada nilai tipeint*
vschar*
.Nilai dalam C bisa berupa tipe pointer. Ini disebut nilai pointer . Agar lebih jelas, nilai pointer bukan pointer dalam bahasa C. Tetapi kita terbiasa mencampurkannya bersama-sama, karena di C itu tidak mungkin ambigu: jika kita menyebut ekspresi
p
sebagai "pointer", itu hanyalah nilai pointer tetapi bukan tipe, karena tipe nama dalam C tidak diungkapkan dengan ekspresi , tetapi dengan nama-jenis atau nama - typedef .Beberapa hal lainnya halus. Sebagai pengguna C, pertama, orang harus tahu apa
object
artinya:Objek adalah entitas untuk mewakili nilai, yang merupakan tipe tertentu. Pointer adalah tipe objek . Jadi jika kita mendeklarasikan
int* p;
, makap
berarti "objek tipe pointer", atau "objek pointer".Catatan tidak ada "variabel" yang secara normatif didefinisikan oleh standar (pada kenyataannya itu tidak pernah digunakan sebagai kata benda oleh ISO C dalam teks normatif). Namun, secara informal, kami menyebut objek sebagai variabel, seperti beberapa bahasa lainnya. (Tapi tetap saja tidak persis seperti itu, misalnya dalam C ++ suatu variabel dapat berupa tipe referensi secara normatif, yang bukan merupakan objek.) Frasa "objek pointer" atau "variabel pointer" kadang-kadang diperlakukan seperti "nilai pointer" seperti di atas, dengan kemungkinan ada sedikit perbedaan. (Satu set contoh lagi adalah "array".)
Karena pointer adalah tipe, dan alamat secara efektif "tidak ada" dalam C, nilai pointer secara kasar "berisi" alamat. Dan ekspresi tipe pointer dapat menghasilkan alamat, misalnya
ISO C11 6.5.2.3
Perhatikan kata-kata ini diperkenalkan oleh WG14 / N1256, yaitu ISO C99: TC3. Di C99 ada
Ini mencerminkan pendapat komite: alamat bukan nilai penunjuk yang dikembalikan oleh
&
operator unary .Meskipun kata-kata di atas, masih ada beberapa kekacauan bahkan dalam standar.
ISO C11 6.6
ISO C ++ 11 5.19
(Draf standar C ++ terbaru menggunakan kata-kata lain sehingga tidak ada masalah ini.)
Sebenarnya baik "alamat konstan" di C dan "alamat ekspresi konstan" di C ++ adalah ekspresi konstan dari tipe pointer (atau setidaknya tipe "pointer-like" sejak C ++ 11).
Dan
&
operator unin builtin disebut sebagai "address-of" dalam C dan C ++; demikian pula,std::addressof
diperkenalkan dalam C ++ 11.Penamaan ini dapat membawa kesalahpahaman. Ekspresi yang dihasilkan adalah jenis pointer, sehingga mereka akan diartikan sebagai: hasilnya mengandung / menghasilkan alamat, daripada adalah sebuah alamat.
sumber
Dikatakan "karena membingungkan mereka yang tidak tahu alamat itu tentang apa" - juga, itu benar: jika Anda mengetahui alamat itu, Anda tidak akan bingung. Secara teoritis, pointer adalah variabel yang menunjuk ke yang lain, praktis memegang alamat, yang merupakan alamat dari variabel yang ditunjuknya. Saya tidak tahu mengapa harus menyembunyikan fakta ini, ini bukan ilmu roket. Jika Anda memahami petunjuk, Anda akan selangkah lebih dekat untuk memahami cara kerja komputer. Lanjutkan!
sumber
Kalau dipikir-pikir, saya pikir ini masalah semantik. Saya tidak berpikir penulisnya benar, karena standar C merujuk pada pointer sebagai memegang alamat ke objek yang direferensikan seperti yang telah disebutkan orang lain di sini. Namun, alamat! = Alamat memori. Sebuah alamat bisa benar-benar apa saja sesuai standar C meskipun akhirnya akan mengarah ke alamat memori, pointer itu sendiri bisa berupa id, offset + selector (x86), benar-benar apa saja asalkan dapat menggambarkan (setelah memetakan) memori apa pun alamat di ruang addressable.
sumber
int i=5
-> i is 5 then, penunjuknya adalah address yes. Juga, null memiliki alamat juga. Biasanya alamat penulisan yang tidak valid (tetapi tidak harus, lihat mode x86-real), tetapi alamatnya tidak kurang. Sebenarnya hanya ada 2 persyaratan untuk null: dijamin untuk membandingkan tidak setara dengan pointer ke objek aktual dan dua pointer nol akan membandingkan sama.p
sebuah pointer,p+1
tidak selalu alamatnya bertambah dengan 1.it's guaranteed to compare unequal to a pointer to an actual object
,. Adapun pointer arithmetics, saya tidak melihat titiknya, nilai pointer masih alamat, bahkan jika operasi "+" tidak perlu menambahkan satu byte ke sana.Salah satu cara lain di mana pointer C atau C ++ berbeda dari alamat memori sederhana karena jenis pointer berbeda yang belum saya lihat di jawaban lain (walaupun diberikan ukuran totalnya, saya mungkin telah mengabaikannya). Tapi itu mungkin yang paling penting, karena bahkan programmer C / C ++ yang berpengalaman pun bisa tersandung:
Compiler mungkin berasumsi bahwa pointer dari tipe yang tidak kompatibel tidak menunjuk ke alamat yang sama bahkan jika mereka jelas melakukannya, yang mungkin memberikan perilaku yang tidak mungkin dilakukan dengan pointer sederhana == model alamat. Pertimbangkan kode berikut (dengan asumsi
sizeof(int) = 2*sizeof(short)
):Perhatikan bahwa ada pengecualian untuk
char*
, jadi memanipulasi nilai menggunakanchar*
dimungkinkan (meskipun tidak terlalu portabel).sumber
Ringkasan cepat: Alamat AC adalah nilai, biasanya direpresentasikan sebagai alamat memori tingkat mesin, dengan tipe tertentu.
Kata "pointer" yang tidak memenuhi syarat bersifat mendua. C memiliki objek pointer (variabel), tipe pointer , ekspresi pointer , dan nilai pointer .
Sangat umum untuk menggunakan kata "pointer" yang berarti "objek pointer", dan itu dapat menyebabkan beberapa kebingungan - itulah sebabnya saya mencoba menggunakan "pointer" sebagai kata sifat dan bukan sebagai kata benda.
Standar C, setidaknya dalam beberapa kasus, menggunakan kata "pointer" yang berarti "nilai pointer". Sebagai contoh, deskripsi malloc mengatakan "mengembalikan pointer nol atau pointer ke ruang yang dialokasikan".
Jadi, apa alamat C? Ini adalah nilai pointer, yaitu nilai dari beberapa tipe pointer tertentu. (Kecuali bahwa nilai pointer nol tidak selalu disebut sebagai "alamat", karena itu bukan alamat apa pun).
Deskripsi standar tentang
&
operator unary mengatakan "memberikan alamat operan". Di luar standar C, kata "alamat" biasanya digunakan untuk merujuk ke alamat memori (fisik atau virtual), biasanya satu kata dalam ukuran (apa pun "kata" pada sistem yang diberikan)."Alamat" AC biasanya diterapkan sebagai alamat mesin - sama seperti
int
nilai C biasanya diterapkan sebagai kata mesin. Tetapi alamat C (nilai penunjuk) lebih dari sekadar alamat mesin. Ini adalah nilai yang biasanya direpresentasikan sebagai alamat mesin, dan itu adalah nilai dengan beberapa tipe tertentu .sumber
Nilai penunjuk adalah alamat. Variabel pointer adalah objek yang dapat menyimpan alamat. Ini benar karena itulah yang menjadi standar penunjuk menjadi. Penting untuk memberi tahu siswa C karena siswa C sering tidak jelas tentang perbedaan antara penunjuk dan hal yang ditunjukkannya (artinya, mereka tidak tahu perbedaan antara amplop dan bangunan). Gagasan alamat (setiap objek memiliki alamat dan itulah yang disimpan oleh pointer) penting karena memilahnya.
Namun, standar berbicara pada tingkat abstraksi tertentu. Orang-orang yang dibicarakan oleh penulis tentang "mengetahui alamat apa yang dimaksud", tetapi yang baru mengenal C, tentu harus telah belajar tentang alamat pada tingkat abstraksi yang berbeda - mungkin dengan pemrograman bahasa assembly. Tidak ada jaminan bahwa implementasi C menggunakan representasi yang sama untuk alamat seperti yang digunakan opcodes CPU (disebut sebagai "alamat toko" dalam bagian ini), yang sudah diketahui orang-orang ini.
Dia kemudian berbicara tentang "manipulasi alamat yang masuk akal". Sejauh menyangkut standar C pada dasarnya tidak ada yang namanya "manipulasi alamat yang masuk akal". Tambahan didefinisikan pada pointer dan pada dasarnya itu adalah itu. Tentu, Anda dapat mengonversi pointer ke integer, melakukan operasi bitwise atau aritmatika, dan kemudian mengubahnya kembali. Ini tidak dijamin berfungsi dengan standar, jadi sebelum menulis kode itu Anda sebaiknya tahu bagaimana implementasi C khusus Anda menunjukkan pointer dan melakukan konversi itu. Ini mungkin menggunakan representasi alamat yang Anda harapkan, tetapi itu tidak bahwa kesalahan Anda karena Anda tidak membaca manual. Itu bukan kebingungan, itu prosedur pemrograman yang salah ;-)
Singkatnya, C menggunakan konsep alamat yang lebih abstrak daripada yang dilakukan penulis.
Konsep penulis tentang alamat tentu saja juga bukan kata tingkat terendah dalam masalah ini. Apa yang dilakukan dengan peta memori virtual dan pengalamatan RAM fisik di berbagai chip, jumlah yang Anda beri tahu CPU adalah "alamat toko" yang ingin Anda akses pada dasarnya tidak ada hubungannya dengan tempat data yang Anda inginkan sebenarnya terletak di perangkat keras. Ini semua lapisan tipuan dan representasi, tetapi penulis telah memilih satu untuk hak istimewa. Jika Anda akan melakukan itu ketika berbicara tentang C, pilih level C untuk mendapat hak istimewa !
Secara pribadi saya tidak berpikir pernyataan penulis sangat membantu, kecuali dalam konteks memperkenalkan C ke programer perakitan. Tentu saja tidak membantu bagi mereka yang berasal dari bahasa tingkat tinggi untuk mengatakan bahwa nilai pointer bukan alamat. Akan jauh lebih baik untuk mengakui kompleksitas daripada mengatakan bahwa CPU memiliki monopoli untuk mengatakan apa alamat itu dan dengan demikian nilai pointer C "bukan" alamat. Mereka adalah alamat, tetapi mereka dapat ditulis dalam bahasa yang berbeda dari alamat yang dia maksud. Membedakan dua hal dalam konteks C sebagai "alamat" dan "alamat toko" akan memadai, saya pikir.
sumber
Sederhananya pointer sebenarnya mengimbangi bagian dari mekanisme segmentasi yang menerjemahkan ke Alamat Linear setelah segmentasi dan kemudian ke alamat fisik setelah paging. Alamat Fisik sebenarnya ditujukan dari Anda ram.
sumber