Apa sebenarnya penunjuk C jika bukan alamat memori?

206

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.

d0rmLife
sumber
118
Pointer adalah variabel yang menyimpan alamat. Ini juga memiliki alamatnya sendiri . Ini adalah perbedaan mendasar antara pointer dan array. Array secara efektif adalah alamat (dan implikasinya, alamat itu sendiri ).
WhozCraig
7
Apa "sumber terpercaya" Anda untuk kutipan itu?
Cornstalks
22
Sumber utama yang dapat dipercaya adalah standar bahasa dan bukan buku yang semi turunan darinya dan semi-ditarik-dari-pengarang-bokong. Saya mempelajarinya dengan cara yang sulit, membuat hampir setiap kesalahan yang saya bisa dan perlahan membangun model mental C agak dekat dengan yang dijelaskan oleh standar dan akhirnya mengganti model tersebut dengan model standar.
Alexey Frunze
9
@thang Orang-orang berpikir pointer = integer karena sering begitu (x86 Linux dan Windows "mengajarkan" itu kepada kami), karena orang-orang suka menggeneralisasi, karena orang tidak tahu standar bahasa dengan baik dan karena mereka memiliki sedikit pengalaman dengan perbedaan yang sangat berbeda. platform. Orang-orang yang sama cenderung berasumsi bahwa pointer ke data dan pointer ke fungsi dapat dikonversi satu sama lain dan data dapat dieksekusi sebagai kode dan kode diakses sebagai data. Meskipun ini mungkin benar pada arsitektur von Neuman (dengan 1 ruang alamat), tetapi tidak selalu benar pada arsitektur Harvard (dengan kode & ruang data).
Alexey Frunze
6
@exebook Standar bukan untuk pemula (terutama yang lengkap). Mereka tidak seharusnya memberikan perkenalan yang lembut dan banyak contoh. Mereka secara resmi mendefinisikan sesuatu, sehingga dapat diimplementasikan dengan benar oleh para profesional.
Alexey Frunze

Jawaban:

148

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.

Alexey Frunze
sumber
34
Tidak banyak yang bisa dijelaskan. Setiap variabel memiliki alamatnya di memori. Tetapi Anda tidak perlu menyimpan alamat mereka di pointer ke mereka. Alih-alih, Anda dapat memberi nomor variabel dari 1 hingga apa pun dan menyimpan nomor itu di dalam pointer. Itu sah menurut standar bahasa selama implementasi tahu bagaimana mengubah angka-angka itu menjadi alamat dan bagaimana melakukan aritmatika penunjuk dengan angka-angka itu dan semua hal lain yang diperlukan oleh standar.
Alexey Frunze
4
saya ingin menambahkan bahwa pada x86, alamat memori terdiri dari pemilih segmen dan offset, sehingga mewakili pointer sebagai segmen: offset masih menggunakan alamat memori.
thang
6
@Lundin Saya tidak punya masalah mengabaikan sifat generik dari standar dan tidak dapat diterapkan ketika saya tahu platform dan kompiler saya. Namun, pertanyaan aslinya bersifat umum, jadi Anda tidak dapat mengabaikan standar saat menjawabnya.
Alexey Frunze
8
@Lundin Anda tidak perlu revolusioner atau ilmuwan. Misalkan Anda ingin meniru mesin 32-bit pada mesin fisik 16-bit dan Anda memperpanjang 64KB RAM Anda hingga 4GB dengan menggunakan penyimpanan disk dan mengimplementasikan pointer 32-bit sebagai offset ke file besar. Pointer itu bukan alamat memori asli.
Alexey Frunze
6
Contoh terbaik yang pernah saya lihat tentang ini adalah implementasi C untuk Symbolics Lisp Machines (sekitar 1990). Setiap objek C diimplementasikan sebagai array Lisp, dan pointer diimplementasikan sebagai pasangan array dan indeks. Karena batas array Lisp diperiksa, Anda tidak pernah bisa melimpah dari satu objek ke objek lainnya.
Barmar
62

Saya tidak yakin tentang sumber Anda, tetapi jenis bahasa yang Anda gambarkan berasal dari standar C:

6.5.3.2 Alamat dan operator tidak langsung
[...]
3. Unary & operator menghasilkan alamat operandnya. [...]

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:

6.2.5 Jenis
[...]
20. [...]
Sebuah jenis pointer dapat berasal dari jenis fungsi atau jenis objek, yang disebut jenis direferensikan . Tipe pointer menggambarkan objek yang nilainya memberikan referensi ke entitas dari tipe yang direferensikan. Tipe penunjuk yang berasal dari tipe T yang dirujuk kadang-kadang disebut '' penunjuk ke T ''. Konstruksi tipe pointer dari tipe referensi disebut '' derivasi tipe pointer ''. Tipe pointer adalah tipe objek yang lengkap.

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:

6.3.2.3 Pointer
[...]
5. Integer dapat dikonversi ke tipe pointer apa pun. Kecuali seperti yang ditentukan sebelumnya, hasilnya adalah implementasi yang ditentukan, mungkin tidak disejajarkan dengan benar, mungkin tidak menunjuk ke entitas dari tipe yang dirujuk, dan mungkin representasi perangkap.

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 ...

Batang jagung
sumber
3
Dalam juru bahasa C, sebuah penunjuk dapat memiliki ID / alamat / non-alamat.
Alexey Frunze
4
@exebook Standar tidak terbatas pada kompilasi C.
Alexey Frunze
7
@Lundin Bravo! Mari kita abaikan standar lebih banyak! Seolah-olah kita belum cukup mengabaikannya dan belum menghasilkan perangkat lunak yang tidak stabil dan buggy karenanya. Juga, tolong jangan bahwa pertanyaan aslinya adalah generik dan karena itu memerlukan jawaban umum.
Alexey Frunze
3
Ketika orang lain mengatakan bahwa pointer mungkin merupakan pegangan atau sesuatu selain alamat, mereka tidak hanya berarti bahwa Anda dapat memaksa data menjadi pointer dengan melemparkan integer ke dalam pointer. Mereka berarti kompiler mungkin menggunakan sesuatu selain alamat memori untuk mengimplementasikan pointer. Pada prosesor Alpha dengan ABI DEC, function pointer bukan alamat fungsi tetapi alamat dari deskriptor fungsi, dan deskriptor berisi alamat fungsi dan beberapa data tentang parameter fungsi. Intinya adalah bahwa standar C sangat fleksibel.
Eric Postpischil
5
@Lundin: Pernyataan bahwa pointer diimplementasikan sebagai alamat integer pada 100% dari sistem komputer yang ada di dunia nyata adalah salah. Komputer ada dengan pengalamatan kata dan pengalamatan segmen-offset. Kompiler masih ada dengan dukungan untuk pointer dekat dan jauh. Komputer PDP-11 ada, dengan RSX-11 dan Task Builder dan overlaynya, di mana pointer harus mengidentifikasi informasi yang diperlukan untuk memuat fungsi dari disk. Pointer tidak dapat memiliki alamat memori suatu objek jika objek tidak ada dalam memori!
Eric Postpischil
39

Pointer vs Variabel

Dalam gambar ini,

pointer_p adalah pointer yang terletak di 0x12345, dan menunjuk ke variabel variable_v di 0x34567.

Harikrishnan
sumber
16
Tidak hanya ini tidak membahas pengertian alamat sebagai lawan dari penunjuk, tetapi secara integral melewatkan titik bahwa alamat bukan hanya bilangan bulat.
Gilles 'SO- berhenti bersikap jahat'
19
-1, ini hanya menjelaskan apa itu pointer. Itu tidak question-- dan Anda menyingkirkan semua kerumitan yang pertanyaannya adalah tentang.
alexis
34

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. NULLadalah pointer yang bukan alamat. Isi dari variabel pointer sebenarnya bisa dari satu dari tiga jenis:

  • yang alamat dari suatu obyek, yang dapat dereferenced (jika pberisi alamat xmaka ekspresi *pmemiliki nilai yang sama seperti x);
  • sebuah null pointer , yang NULLmerupakan contoh;
  • konten yang tidak valid , yang tidak menunjuk ke suatu objek (jika ptidak memiliki nilai yang valid, maka *pdapat 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 padalah pointer ke int( int *p;), maka p + 1menunjuk ke integer yang sizeof(int)byte setelah p(dengan asumsi p + 1masih pointer yang valid). Jika qadalah pointer ke charyang menunjuk ke alamat yang sama dengan p( char *q = p;), maka q + 1bukan alamat yang sama dengan p + 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 memiliki p == qmeskipun pdan qmemiliki 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

  • itu hanya valid, pointer bukan nol yang merupakan alamat;
  • Anda dapat memiliki beberapa alamat untuk lokasi yang sama;
  • Anda tidak dapat melakukan aritmatika pada alamat, dan tidak ada urutannya;
  • pointer juga membawa informasi tipe.

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 intmemerlukan perataan 4-byte, 0x7654321 tidak dapat menjadi int*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:

unsigned int x = 0;
unsigned short *p = (unsigned short*)&x;
p[0] = 1;
printf("%u = %u\n", x, *p);

Anda mungkin mengharapkan itu pada mesin run-of-the-mill di mana sizeof(int)==4dan sizeof(short)==2, ini bisa mencetak 1 = 1?(little-endian) atau 65536 = 1?(big-endian). Tetapi pada PC Linux 64-bit saya dengan GCC 4.4:

$ c99 -O2 -Wall a.c && ./a.out 
a.c: In function main’:
a.c:6: warning: dereferencing pointer p does break strict-aliasing rules
a.c:5: note: initialized from here
0 = 1?

GCC cukup berbaik hati untuk memperingatkan kita apa yang salah dalam contoh sederhana ini - dalam contoh yang lebih kompleks, kompiler mungkin tidak memperhatikan. Karena pmemiliki tipe yang berbeda &x, mengubah ppoin apa yang tidak dapat memengaruhi &xpoin apa (di luar beberapa pengecualian yang ditentukan dengan baik). Oleh karena itu kompiler bebas untuk menyimpan nilai xdalam register dan tidak memperbarui register ini sebagai *pperubahan. 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.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
5
+1. Jawaban lain tampaknya ketinggalan bahwa pointer dilengkapi dengan informasi jenis. Ini jauh lebih penting daripada alamat / ID / diskusi apa pun.
undur_gongor
+1 Poin bagus tentang informasi jenis. Saya tidak yakin contoh kompiler benar tho ... Tampaknya sangat tidak mungkin, misalnya, yang *p = 3dijamin berhasil ketika p belum diinisialisasi.
LarsH
@ LarsH Anda benar, terima kasih, bagaimana saya menulis itu? Saya menggantinya dengan contoh yang bahkan menunjukkan perilaku mengejutkan pada PC saya.
Gilles 'SANGAT berhenti menjadi jahat'
1
um, NULL adalah ((batal *) 0) ..?
Aniket Inge
1
@ gnasher729 Penunjuk nol adalah penunjuk. NULLtidak, tetapi untuk tingkat detail yang diperlukan di sini, ini adalah gangguan yang tidak relevan. Bahkan untuk pemrograman sehari-hari, fakta yang NULLdapat diimplementasikan sebagai sesuatu yang tidak mengatakan "pointer" tidak sering muncul (terutama beralih NULLke fungsi variadik - tetapi bahkan di sana, jika Anda tidak melemparkannya , Anda sudah membuat asumsi bahwa semua tipe pointer memiliki representasi yang sama).
Gilles 'SO- berhenti bersikap jahat'
19

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:

int q = 10; /*say q is at address 0x10203040*/
int *p = &q; /*means let p contain the address of q, which is 0x10203040*/
*p = 20; /*set whatever is at the address pointed by "p" as 20*/

Itu dia. Sesederhana itu.

masukkan deskripsi gambar di sini

Sebuah program untuk menunjukkan apa yang saya katakan dan hasilnya ada di sini:

http://ideone.com/rcSUsb

Program:

#include <stdio.h>

int main(int argc, char *argv[])
{
  /* POINTER AS AN ADDRESS */
  int q = 10;
  int *p = &q;

  printf("address of q is %p\n", (void *)&q);
  printf("p contains %p\n", (void *)p);

  p = NULL;
  printf("NULL p now contains %p\n", (void *)p);
  return 0;
}
Aniket Inge
sumber
5
Itu dapat membingungkan bahkan lebih. Alice, bisakah kamu melihat kucing? Tidak, saya hanya bisa melihat senyum kucing. Jadi mengatakan bahwa pointer adalah alamat atau pointer adalah variabel yang menyimpan alamat atau mengatakan bahwa pointer adalah nama konsep yang merujuk pada ide alamat, sejauh mana penulis buku bisa membingungkan neeeewbies yang membingungkan?
exebook
@exebook bagi mereka yang berpengalaman dalam pointer, cukup sederhana. Mungkin gambar akan membantu?
Aniket Inge
5
Pointer tidak harus berisi alamat. Dalam juru bahasa C, itu bisa berupa sesuatu yang lain, semacam ID / pegangan.
Alexey Frunze
"Label" atau nama variabel adalah kompiler / assembler dan tidak ada di tingkat mesin jadi saya tidak berpikir itu akan muncul di memori.
Ben
1
@Aniket Variabel pointer dapat berisi nilai pointer. Anda hanya perlu menyimpan hasil fopenke dalam variabel jika Anda perlu menggunakannya lebih dari sekali (yang, untuk fopen, hampir sepanjang waktu).
Gilles 'SO- berhenti bersikap jahat'
16

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,

 int x;
 int* y = &x;
 char* z = &x;

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)).

thang
sumber
1
+1 untuk menunjukkan aritmatika pointer tidak sama dengan aritmatika pada alamat.
kutschkem
Dalam kasus 16-bit 8086, alamat memori dijelaskan oleh basis segmen + offset, keduanya 16 bit. Ada banyak kombinasi basis segmen + offset yang memberikan alamat yang sama dalam memori. Ini farpointer bukan hanya "integer".
vonbrand
@vonbrand saya tidak mengerti mengapa Anda mengirim komentar itu. masalah itu telah dibahas sebagai komentar di bawah jawaban lain. hampir setiap jawaban lainnya menganggap bahwa address = integer dan apa pun yang bukan integer bukanlah address. saya hanya menunjukkan ini dan perhatikan bahwa itu mungkin atau mungkin tidak benar. seluruh poin saya dalam jawabannya adalah bahwa itu tidak relevan. semuanya hanya bertele-tele, dan masalah utama tidak dibahas dalam jawaban lain.
thang
@tang, ide "pointer == address" salah . Bahwa semua orang dan bibi favorit mereka terus berkata begitu tidak membuatnya benar.
vonbrand
@vonbrand, dan mengapa Anda membuat komentar itu di bawah posting saya? Saya tidak mengatakan itu benar atau salah. Sebenarnya, itu benar dalam skenario / asumsi tertentu, tetapi tidak selalu. Biarkan saya meringkas lagi titik tulisan (untuk kedua kalinya). seluruh poin saya dalam jawabannya adalah bahwa itu tidak relevan. semuanya hanya bertele-tele, dan masalah utama tidak dibahas dalam jawaban lain. akan lebih tepat untuk mengomentari jawaban yang membuat klaim bahwa pointer == address atau address == integer. lihat komentar saya di bawah posting Alexey sehubungan dengan segmen: offset.
thang
15

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.

Alexis
sumber
12

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:

union {
    int i;
    char c;
} u;

Anda dapat memiliki tiga petunjuk berbeda yang menunjuk ke objek yang sama:

void *v = &u;
int *i = &u.i;
char *c = &u.c;

Jika Anda membandingkan nilai pointer ini, semuanya sama:

v==i && i==c

Namun, jika Anda menambah setiap pointer, Anda akan melihat bahwa tipe yang mereka tunjuk menjadi relevan.

i++;
c++;
// You can't perform arithmetic on a void pointer, so no v++
i != c

Variabel idan cakan memiliki nilai yang berbeda pada titik ini, karena i++menyebabkan imengandung alamat integer yang dapat diakses berikutnya, dan c++menyebabkan cmenunjuk ke karakter berikutnya yang dapat dialamatkan. Biasanya, bilangan bulat mengambil lebih banyak memori daripada karakter, sehingga iakan berakhir dengan nilai yang lebih besar daripada csetelah keduanya bertambah.

Mark Bessey
sumber
2
+1 Terima kasih. Dengan pointer, nilai dan tipe sama tidak terpisahkannya dengan seseorang dapat memisahkan tubuh pria dari jiwanya.
Aki Suihkonen
i == ctidak 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).
MM
8

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 variabel

Dalam 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.

Aki Suihkonen
sumber
8

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.

exebook
sumber
PC ... model memori datar? apa itu penyeleksi?
thang
Riight. Dan ketika perubahan arsitektur berikutnya muncul, mungkin dengan kode terpisah dan ruang data, atau seseorang kembali ke arsitektur segmen yang terhormat (yang masuk akal untuk keamanan, bahkan mungkin menambahkan beberapa kunci ke nomor segmen + offset untuk memeriksa izin) Anda indah "pointer hanya bilangan bulat" datang runtuh.
vonbrand
7

Pointer, seperti variabel lain dalam C, pada dasarnya adalah kumpulan bit yang dapat diwakili oleh satu atau lebih unsigned charnilai-nilai yang disatukan (seperti dengan jenis cariable lainnya, sizeof(some_variable)akan menunjukkan jumlah unsigned charnilai). 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 foodan baradalah array dari 10 item yang disimpan secara berurutan dalam memori, sebuah penunjuk ke item "kesebelas" darifoomungkin malah menunjuk ke item pertama bar, tetapi dalam sistem di mana setiap "pointer" adalah ID objek dan offset, sistem dapat menjebak jika kode mencoba mengindeks pointer ke fooluar 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 charnilai. 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 melakukannyacallocbeberapa 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 dalam callocmemori 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.

supercat
sumber
Pada overhead yang bagus, mungkin Anda dapat mendeteksi penggunaan nilai pointer yang mungkin "membocorkan" nilai numeriknya, dan menyematkan alokasi sehingga pengumpul sampah tidak akan mengumpulkan atau memindahkannya (kecuali jika freedisebut 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 yang char*tidak diketahui asalnya harus diperiksa.
Steve Jessop
@SteveJessop: Saya pikir desain seperti itu akan lebih buruk daripada tidak berguna, karena tidak mungkin bagi kode untuk mengetahui pointer apa yang perlu dibebaskan. Pengumpul sampah yang menganggap sesuatu yang terlihat seperti pointer adalah orang yang mungkin terlalu konservatif, tetapi umumnya hal-hal yang terlihat seperti - tetapi tidak - pointer memiliki kemungkinan untuk berubah, sehingga menghindari kebocoran memori "permanen". Memiliki tindakan yang terlihat seperti membusuk pointer menjadi byte secara permanen membekukan pointer adalah resep yang dijamin untuk kebocoran memori.
supercat
Saya pikir itu akan gagal karena alasan kinerja - jika Anda ingin kode Anda berjalan lambat karena setiap akses diperiksa maka jangan tulis dalam C ;-) Saya memiliki harapan yang lebih tinggi untuk kecerdikan programmer C daripada Anda, karena saya pikir sementara tidak nyaman itu mungkin tidak masuk akal untuk menghindari menyematkan alokasi yang tidak perlu. Bagaimanapun, C ++ mendefinisikan "pointer yang diturunkan dengan aman" tepatnya untuk menangani masalah ini, jadi kami tahu apa yang harus dilakukan jika kami ingin meningkatkan abstrak pointer C ke tingkat di mana mereka mendukung pengumpulan sampah yang cukup efektif.
Steve Jessop
@SteveJessop: Agar sistem GC bermanfaat, ia harus dapat melepaskan memori yang andal yang freebelum 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 ...
supercat
... untuk kode yang valid C ++, tetapi yang kompilernya tidak dapat membuktikan bahwa pointer tidak akan pernah dapat dikonversi menjadi bentuk yang tidak dapat dikenali, saya tidak melihat bagaimana orang dapat menghindari risiko bahwa sebuah program yang pada kenyataannya tidak pernah menggunakan pointer sebagai bilangan bulat mungkin secara keliru dianggap melakukan hal itu.
supercat
6

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.

Matthew Sanders
sumber
1
Secara umum, ini adalah pegangan / ID. Biasanya, ini alamat biasa.
Alexey Frunze
Saya menyesuaikan jawaban saya menjadi PC yang lebih banyak dengan definisi Handle di wikipedia. Saya suka merujuk ke pointer sebagai contoh khusus dari pegangan, karena pegangan mungkin hanya referensi ke pointer.
Matthew Sanders
6

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 alamat T.

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

LinearAddress = SegmentRegister[SegNum].base << 4 + Offset

Padahal dalam mode terproteksi, mungkin saja

LinearAddress = SegmentRegister[SegNum].base + offset

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 :

Ada banyak cara untuk menyelesaikan ini [masalah yang terkait dengan inheitance tunggal versus ganda, dan warisan virtual]. Inilah cara kompiler Visual Studio memutuskan untuk menanganinya: Sebuah pointer ke fungsi anggota dari kelas multiply-inherited benar-benar sebuah struktur. "Dan mereka melanjutkan dengan mengatakan" Casting pointer fungsi dapat mengubah ukurannya! ".

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.

Krazy Glew
sumber
Dalam mode real 16 bit LinearAddress = SegmentRegister.Selector * 16 + Offset(perhatikan waktu 16, tidak bergeser 16). Dalam mode terproteksi LinearAddress = SegmentRegister.base + offset(tidak ada penggandaan apa pun; basis segmen disimpan di GDT / LDT dan di-cache dalam register segmen apa adanya ).
Alexey Frunze
Anda juga benar tentang basis segmen. Saya salah ingat. Ini adalah batas segmen yang secara opsional dikalikan dengan 4K. Base segment hanya perlu diacak oleh perangkat keras saat memuat deskriptor segmen dari memori ke register segmen.
Krazy Glew
4

Pointer hanyalah variabel lain yang digunakan untuk menyimpan alamat lokasi memori (biasanya alamat memori variabel lain).

Tuxdude
sumber
Jadi, orang yang dituju sebenarnya alamat memori? Anda tidak setuju dengan penulis? Hanya berusaha mengerti.
d0rmLife
Fungsi utama dari pointer adalah menunjuk ke sesuatu. Bagaimana persisnya itu dicapai dan apakah ada alamat asli atau tidak, tidak ditentukan. Pointer bisa berupa ID / pegangan, bukan alamat asli.
Alexey Frunze
4

Anda bisa melihatnya dengan cara ini. Pointer adalah nilai yang mewakili alamat di ruang memori addressable.

Valentin Radu
sumber
2
Pointer tidak harus memiliki alamat memori asli di dalamnya. Lihat jawaban saya dan komentar di bawahnya.
Alexey Frunze
apa .... pointer ke variabel pertama pada tumpukan tidak mencetak 0. ia mencetak bagian atas (atau bawah) dari bingkai tumpukan tergantung bagaimana itu diterapkan.
thang
@thang Untuk variabel pertama, bagian atas dan bawah sama. Dan apa alamat bagian atas atau bawah dalam kasus tumpukan ini?
Valentin Radu
@ValentinRadu, mengapa Anda tidak mencobanya .. jelas Anda belum mencobanya.
thang
2
@thang Anda benar, saya melakukan beberapa asumsi yang sangat buruk, untuk pembelaan saya jam 5 pagi di sini.
Valentin Radu
3

Pointer hanyalah variabel lain yang dapat berisi alamat memori biasanya dari variabel lain. Pointer yang menjadi variabel juga memiliki alamat memori.

Xavier DSouza
sumber
1
Belum tentu alamat. Btw, apakah Anda membaca jawaban dan komentar yang ada sebelum memposting jawaban Anda?
Alexey Frunze
3

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 *ppointer menunjuk ke suatu long nvariabel, dan *p = 0.0dieksekusi, kompiler tidak diharuskan untuk menangani ini. Penggunaan selanjutnya ntidak perlu membaca pola bit dari nilai float, tetapi mungkin, itu akan menjadi akses yang dioptimalkan yang didasarkan pada asumsi "aliasing ketat" yang nbelum tersentuh! Artinya, asumsi bahwa program itu berperilaku baik, dan karenanya ptidak boleh menunjuk n.

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.

Kaz
sumber
Pointer NULL tidak berfungsi seperti yang Anda pikirkan pada semua platform - silakan lihat balasan saya untuk CiscoIPPhone di atas. NULL == 0 adalah asumsi yang hanya berlaku pada platform berbasis x86. Konvensi mengatakan bahwa platform baru harus cocok dengan x86, namun khususnya di dunia yang tertanam ini tidak demikian. Sunting: Selain itu, C tidak melakukan apa pun untuk mengabstraksikan nilai penunjuk arah dari perangkat keras - "ptr! = 0" tidak akan berfungsi sebagai tes NULL pada platform tempat NULL! = 0.
DX-MON
1
DX-MON, itu sepenuhnya salah untuk standar C. NULL dikhususkan untuk menjadi 0, dan mereka dapat digunakan secara bergantian dalam pernyataan. Apakah pada bukan representasi pointer NULL dalam perangkat keras adalah semua 0 bit tidak relevan dengan bagaimana itu diwakili dalam kode sumber.
Mark Bessey
@ DX-MON Saya khawatir Anda tidak bekerja dengan fakta yang benar. Dalam C, ekspresi konstanta integral berfungsi sebagai konstanta penunjuk nol, terlepas dari apakah penunjuk nol adalah alamat nol. Jika Anda mengetahui kompiler C ptr != 0yang bukan merupakan tes nol, harap ungkapkan identitasnya (tetapi sebelum Anda melakukannya, kirim laporan bug ke vendor).
Kaz
Saya mengerti apa yang Anda maksud, tetapi komentar Anda tentang null pointer tidak koheren karena Anda membingungkan pointer dan alamat memori - persis apa yang dikutip dalam pertanyaan yang disarankan untuk dihindari! Pernyataan yang benar: C mendefinisikan pointer nol menjadi nol, terlepas dari apakah alamat memori pada offset nol sah atau tidak.
alexis
1
@alexis, Bab dan ayat. C tidak mendefinisikan pointer nol menjadi nol. C mendefinisikan nol (atau ekspresi konstanta integral mana pun yang nilainya nol) sebagai sintaks untuk menunjukkan konstanta penunjuk nol. faqs.org/faqs/C-faq/faq (bagian 5).
Kaz
3

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 Cdengan tipe yang disebut pointerkontennya ditafsirkan sebagai alamat objek yang mendukung operasi berikut.

+ : A variable of type integer (usually called offset) can be added to yield a new pointer
- : A variable of type integer (usually called offset) can be subtracted to yield a new pointer
  : A variable of type pointer can be subtracted to yield an integer (usually called offset)
* : De-referencing. Retrieve the value of the variable (called address) and map to the object the address refers to.
++: It's just `+= 1`
--: It's just `-= 1`

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.

Abhijit
sumber
Saya suka penjelasan Anda tentang realitas abstrak dari penunjuk umum dalam sistem umum. Tapi, mungkin mendiskusikan memori akan sangat membantu. Bahkan, berbicara sendiri, saya tahu itu akan ...! Saya pikir mendiskusikan koneksi bisa sangat membantu untuk memahami gambaran besarnya. +1 lagian :)
d0rmLife
@ d0rmLife: Anda memiliki penjelasan yang cukup dalam jawaban lain yang mencakup gambaran yang lebih besar. Saya hanya ingin memberikan penjelasan abstrak matematika sebagai pandangan lain. Juga IMHO, itu akan membuat lebih sedikit kebingungan dalam memanggil &sebagai 'Alamat' karena itu lebih terkait dengan Obyek daripada pointer per se`
Abhijit
Jangan tersinggung, tapi saya akan memutuskan sendiri apa penjelasan yang cukup. Satu buku teks tidak cukup untuk sepenuhnya menjelaskan struktur data dan alokasi memori. ;) .... Bagaimanapun, jawaban Anda masih membantu , meskipun itu bukan novel.
d0rmLife
Tidak masuk akal untuk menangani pointer tanpa konsep memori . Jika objek ada tanpa memori, itu harus di suatu tempat, di mana tidak ada alamat - misalnya di register. Untuk dapat menggunakan '&' mengandaikan memori.
Aki Suihkonen
3

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:

makna yang tepat dari isi suatu objek ketika diartikan memiliki jenis tertentu

(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 tipe int*vs char*.

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 psebagai "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 objectartinya:

wilayah penyimpanan data di lingkungan eksekusi, yang isinya dapat mewakili nilai

Objek adalah entitas untuk mewakili nilai, yang merupakan tipe tertentu. Pointer adalah tipe objek . Jadi jika kita mendeklarasikan int* p;, maka pberarti "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

3 &Operator unary menghasilkan alamat operandnya.

Perhatikan kata-kata ini diperkenalkan oleh WG14 / N1256, yaitu ISO C99: TC3. Di C99 ada

3 &Operator unary mengembalikan alamat operandnya.

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

9 Konstanta alamat adalah pointer nol, pointer ke nilai yang menunjuk objek durasi penyimpanan statis, atau pointer ke penunjuk fungsi

ISO C ++ 11 5.19

3 ... Ekspresi konstanta alamat adalah ekspresi konstanta inti prvalue dari tipe pointer yang mengevaluasi ke alamat objek dengan durasi penyimpanan statis, ke alamat fungsi, atau ke nilai pointer nol, atau ekspresi konstanta inti prvalue jenis std::nullptr_t. ...

(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::addressofdiperkenalkan 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.

FrankHB
sumber
2

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!

ern0
sumber
2

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.

Valentin Radu
sumber
Pointer menyimpan alamat (atau tidak, jika itu nol). Tapi itu jauh berbeda dari itu menjadi alamat: misalnya, dua petunjuk ke alamat yang sama tetapi dengan jenis yang berbeda tidak setara dalam banyak situasi.
Gilles 'SO- berhenti bersikap jahat'
@Gilles Jika Anda melihat "being", seperti pada 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.
Valentin Radu
Sebaliknya, pointer nol dijamin tidak sama dengan alamat objek apa pun. Mendereferensi pointer nol adalah perilaku yang tidak terdefinisi. Masalah besar dengan mengatakan bahwa "pointer adalah alamat" adalah bahwa mereka bekerja secara berbeda. Jika psebuah pointer, p+1tidak selalu alamatnya bertambah dengan 1.
Gilles 'SO- stop being evil'
Baca lagi komentarnya tolong 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.
Valentin Radu
1

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)):

unsigned int i = 0;
unsigned short* p = (unsigned short*)&i;
p[0]=p[1]=1;

if (i == 2 + (unsigned short)(-1))
{
  // you'd expect this to execute, but it need not
}

if (i == 0)
{
  // you'd expect this not to execute, but it actually may do so
}

Perhatikan bahwa ada pengecualian untuk char*, jadi memanipulasi nilai menggunakan char*dimungkinkan (meskipun tidak terlalu portabel).

celtschk
sumber
0

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 intnilai 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 .

Keith Thompson
sumber
0

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.

Steve Jessop
sumber
0

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.

       Selector  +--------------+         +-----------+
      ---------->|              |         |           |
                 | Segmentation | ------->|  Paging   |
        Offset   |  Mechanism   |         | Mechanism |
      ---------->|              |         |           |
                 +--------------+         +-----------+
        Virtual                   Linear                Physical
router
sumber