Saya tahu bahwa pointer menyimpan alamat. Saya tahu bahwa tipe pointer "umumnya" dikenal berdasarkan "tipe" data yang mereka tunjuk. Tapi, pointer masih variabel dan alamat yang mereka pegang harus memiliki "tipe" data. Menurut info saya, alamat berada dalam format heksadesimal. Tapi, saya masih belum tahu apa "tipe" data heksadesimal ini. (Perhatikan bahwa saya tahu apa heksadesimal itu, tetapi ketika Anda mengatakan 10CBA20
, misalnya, apakah rangkaian karakter ini? Bilangan bulat? Apa? Ketika saya ingin mengakses alamat dan memanipulasinya .. itu sendiri, saya perlu tahu tipenya. Ini itulah sebabnya saya bertanya.)
30
Jawaban:
Jenis variabel pointer adalah .. pointer.
Operasi yang secara formal diperbolehkan untuk Anda lakukan dalam C adalah membandingkannya (dengan pointer lain, atau nilai NULL / nol khusus), untuk menambah atau mengurangi integer, atau melemparkannya ke pointer lain.
Setelah Anda menerima perilaku yang tidak terdefinisi , Anda dapat melihat apa nilainya sebenarnya. Biasanya akan menjadi kata mesin, hal yang sama dengan integer, dan biasanya dapat dilemparkan ke dan dari tipe integer. (Cukup banyak kode Windows yang melakukan ini dengan menyembunyikan pointer di DWORD atau typedef HANDLE).
Ada beberapa arsitektur di mana pointer tidak sederhana karena ingatannya tidak datar. DOS / 8086 'dekat' dan 'jauh'; Ruang memori dan kode PIC berbeda.
sumber
p1-p2
. Hasilnya adalah nilai integral yang ditandatangani. Secara khusus,&(array[i])-&(array[j]) == i-j
intptr_t
danuintptr_t
yang dijamin "cukup besar" untuk nilai pointer.p
specifier ke printf membuat mendapatkan representasi void pointer yang dapat dibaca manusia menjadi didefinisikan, jika implementasi bergantung pada perilaku dalam c.Anda terlalu rumit.
Alamat hanyalah bilangan bulat, titik. Idealnya mereka adalah jumlah sel memori yang dirujuk (dalam praktiknya ini menjadi lebih rumit karena segmen, memori virtual dll.).
Sintaks heksadesimal adalah fiksi lengkap yang hanya ada untuk kenyamanan programmer. 0x1A dan 26 adalah jumlah yang persis sama dari jenis yang persis sama , dan tidak juga apa yang digunakan komputer - secara internal, komputer selalu menggunakan 00011010 (serangkaian sinyal biner).
Apakah kompiler memungkinkan Anda memperlakukan pointer karena angka tergantung pada definisi bahasa - bahasa "pemrograman sistem" secara tradisional lebih transparan tentang cara kerja sesuatu di bawah tenda, sementara bahasa "tingkat tinggi" lebih sering mencoba menyembunyikan bare metal dari programmer - tetapi itu tidak mengubah apa pun tentang fakta bahwa pointer adalah angka, dan biasanya tipe angka yang paling umum (angka dengan bit sebanyak arsitektur prosesor Anda).
sumber
Pointer hanya itu - pointer. Itu bukan sesuatu yang lain. Jangan mencoba untuk berpikir bahwa itu adalah sesuatu yang lain.
Dalam bahasa seperti C, C ++, dan Objective-C, pointer data memiliki empat jenis nilai yang mungkin:
Ada juga pointer fungsi, yang mengidentifikasi fungsi, atau pointer fungsi nol, atau memiliki nilai tak tentu.
Pointer lainnya adalah "pointer to member" di C ++. Ini jelas bukan alamat memori! Sebaliknya, mereka mengidentifikasi anggota dari setiap instance dari kelas. Di Objective-C, Anda memiliki pemilih, yang merupakan sesuatu seperti "penunjuk ke metode instance dengan nama metode dan nama argumen yang diberikan". Seperti pointer anggota, ini mengidentifikasi semua metode dari semua kelas selama mereka terlihat sama.
Anda dapat menyelidiki bagaimana kompiler spesifik mengimplementasikan pointer, tetapi itu adalah pertanyaan yang sama sekali berbeda.
sumber
class A { public: int num; int x; }; int A::*pmi = &A::num; A a; int n = a.*pmi;
Variabelpmi
tidak akan banyak digunakan jika tidak berisi alamat memori, yaitu, ketika baris terakhir kode menetapkan, alamat anggotanum
instancea
dari kelasA
. Anda bisa melemparkan ini keint
pointer biasa (walaupun kompiler mungkin akan memberi Anda peringatan) dan berhasil melakukannya (membuktikan bahwa itu adalah gula sintaksis untuk pointer lainnya).Pointer adalah pengalamatan pola bit (mengidentifikasi secara unik untuk tujuan membaca atau menulis) kata penyimpanan dalam RAM. Untuk alasan historis dan konvensional, unit pembaruan adalah delapan bit, yang dikenal dalam bahasa Inggris sebagai "byte" atau dalam bahasa Prancis, agak lebih logis, sebagai oktet. Ini ada di mana-mana tetapi tidak melekat; ukuran lain sudah ada.
Jika saya ingat benar ada satu komputer yang menggunakan kata 29bit; tidak hanya ini bukan kekuatan dua, itu bahkan prima. Saya pikir ini SILLIAC tetapi artikel Wikipedia yang bersangkutan tidak mendukung ini. CAN BUS menggunakan 29 bit alamat tetapi dengan konvensi alamat jaringan tidak disebut sebagai pointer bahkan ketika mereka secara fungsional identik.
Orang-orang terus menyatakan bahwa pointer adalah bilangan bulat. Ini bukan intrinsik atau esensial, tetapi jika kita menafsirkan pola bit sebagai bilangan bulat kualitas berguna ordinalitas muncul, memungkinkan implementasi yang sangat langsung (dan karena itu efisien pada perangkat keras kecil) konstruksi seperti "string" dan "array". Gagasan memori yang berdekatan tergantung pada kedekatan ordinal, dan penentuan posisi relatif dimungkinkan; operasi perbandingan bilangan bulat dan aritmatika dapat diterapkan secara bermakna. Untuk alasan ini hampir selalu ada korelasi kuat antara ukuran kata untuk penyimpanan alamat dan ALU (hal yang melakukan integer matematika).
Terkadang keduanya tidak berkorespondensi. Pada PC awal, bus alamat memiliki lebar 24 bit.
sumber
char*
mis. Untuk keperluan penyalinan / pembandingan memori, dansizeof char==1
sebagaimana didefinisikan oleh standar C), bukan kata (kecuali ukuran kata CPU sama dengan ukuran byte).Pada dasarnya setiap komputer modern adalah mesin yang mendorong sedikit. Biasanya itu mendorong bit di dalam kelompok data, yang disebut byte, kata, kata atau qwords.
Satu byte terdiri dari 8 bit, satu kata 2 byte (atau 16 bit), satu kata 2 kata (atau 32 bit) dan satu kata kata 2 kata (atau 64 bit). Ini bukan satu-satunya cara untuk mengatur bit. Manipulasi 128 bit dan 256 bit juga terjadi, seringkali dalam instruksi SIMD.
Instruksi perakitan beroperasi pada register dan alamat memori biasanya beroperasi dalam salah satu formulir di atas.
ALU (unit aritmatika logika) beroperasi pada kumpulan bit seperti jika mereka mewakili bilangan bulat (biasanya format Komplemen Dua), dan FPU seolah-olah mereka di mana nilai-nilai titik mengambang (biasanya gaya IEEE 754
float
dandouble
). Bagian lain akan bertindak seolah-olah mereka adalah kumpulan data dari beberapa format, karakter, entri tabel, instruksi CPU, atau alamat.Pada komputer 64 bit yang khas, bundel 8 byte (64 bit) adalah alamat. Kami menampilkan alamat ini secara konvensional dalam format hex (seperti
0xabcd1234cdef5678
), tetapi itu hanya cara mudah bagi manusia untuk membaca pola bit. Setiap byte (8 bit) ditulis sebagai dua karakter hex (ekuivalen setiap hex karakter - 0 hingga F - mewakili 4 bit).Apa yang sebenarnya terjadi (untuk beberapa tingkat sebenarnya) adalah bahwa ada bit, biasanya disimpan dalam register atau disimpan di lokasi yang berdekatan di bank memori, dan kami hanya mencoba menggambarkannya kepada manusia lain.
Mengikuti pointer terdiri dari meminta pengontrol memori untuk memberi kami beberapa data di lokasi itu. Anda biasanya akan meminta pengontrol memori untuk sejumlah byte di lokasi tertentu (well, secara implisit berbagai lokasi, biasanya berdekatan), dan dikirimkan melalui berbagai mekanisme yang tidak akan saya bahas.
Kode biasanya menentukan tujuan untuk data yang akan diambil - register, alamat memori lain, dll - dan biasanya itu adalah ide yang buruk untuk memuat data floating point ke dalam register yang mengharapkan integer, atau sebaliknya.
Jenis data dalam C / C ++ adalah sesuatu yang dicatat oleh kompiler, dan itu mengubah kode apa yang dihasilkan. Biasanya tidak ada yang intrinsik dalam data yang membuatnya benar - benar dari jenis apa pun. Hanya kumpulan bit (disusun dalam bytes) yang dimanipulasi dengan cara seperti integer (atau cara seperti float, atau cara seperti alamat) oleh kode.
Ada pengecualian untuk ini. Ada arsitektur di mana hal-hal tertentu berbeda jenis bit. Contoh paling umum adalah halaman eksekusi yang dilindungi - sementara instruksi memberitahu CPU apa yang dilakukan adalah bit, pada saat run time (memori) halaman yang berisi kode untuk dieksekusi ditandai secara khusus, tidak dapat dimodifikasi, dan Anda tidak dapat mengeksekusi halaman yang tidak ditandai. sebagai halaman eksekusi.
Ada juga hanya baca data (kadang-kadang disimpan dalam ROM yang secara fisik tidak dapat ditulis ke!), Masalah penyelarasan (beberapa prosesor tidak dapat memuat
double
dari memori kecuali mereka disejajarkan dengan cara tertentu, atau instruksi SIMD yang memerlukan penyelarasan tertentu), dan berjuta-juta kebiasaan arsitektur lainnya.Bahkan tingkat detail di atas adalah bohong. Komputer tidak "benar-benar" mendorong bit, mereka benar-benar mendorong voltase dan arus. Tegangan dan arus ini terkadang tidak melakukan apa yang "seharusnya" dilakukan pada tingkat abstraksi bit. Chip dirancang untuk mendeteksi sebagian besar kesalahan seperti itu dan memperbaikinya tanpa abstraksi tingkat yang lebih tinggi harus menyadarinya.
Bahkan itu bohong.
Setiap level abstraksi menyembunyikan yang di bawah ini, dan memungkinkan Anda berpikir tentang menyelesaikan masalah tanpa harus mengingat diagram Feynman untuk dicetak
"Hello World"
.Jadi pada tingkat kejujuran yang cukup, komputer mendorong bit, dan bit-bit itu diberi makna oleh bagaimana mereka digunakan.
sumber
Orang-orang telah membuat masalah besar apakah pointer bilangan bulat atau tidak. Sebenarnya ada jawaban untuk pertanyaan-pertanyaan ini. Namun, Anda harus mengambil langkah ke tanah spesifikasi, yang bukan untuk menjadi lemah hati. Kita akan melihat spesifikasi C, ISO / IEC 9899: TC2
6.3.2.3 Petunjuk
Integer dapat dikonversi ke tipe pointer apa saja. 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.
Setiap tipe pointer dapat dikonversi ke tipe integer. Kecuali seperti yang ditentukan sebelumnya, hasilnya ditentukan oleh implementasi. Jika hasilnya tidak dapat direpresentasikan dalam tipe integer, perilaku tidak terdefinisi. Hasilnya tidak harus dalam kisaran nilai dari semua tipe integer.
Sekarang untuk ini, Anda perlu mengetahui beberapa istilah spesifikasi umum. "implementasi terdefinisi" berarti setiap kompiler diperbolehkan untuk mendefinisikannya secara berbeda. Bahkan, kompiler bahkan dapat mendefinisikannya dengan cara yang berbeda tergantung pada pengaturan kompiler Anda. Perilaku tidak terdefinisi berarti kompiler diperbolehkan untuk melakukan apa saja, mulai dari memberikan kesalahan waktu kompilasi hingga perilaku yang tidak dapat dijelaskan, hingga bekerja dengan sempurna.
Dari sini kita dapat melihat bahwa bentuk penyimpanan yang mendasarinya tidak ditentukan, selain itu mungkin ada konversi ke tipe integer. Sejujurnya, setiap kompiler di bawah matahari mewakili pointer di bawah kap sebagai alamat integer (dengan beberapa kasus khusus di mana ia dapat direpresentasikan sebagai 2 integer, bukan hanya 1), tetapi spesifikasi memungkinkan benar-benar apa pun, seperti mewakili alamat sebagai string 10 karakter!
Jika kami maju cepat dari C dan melihat spesifikasi C ++, kami mendapatkan sedikit lebih jelas
reinterpret_cast
, tetapi ini adalah bahasa yang berbeda, sehingga nilainya untuk Anda dapat bervariasi:Spesifikasi draft ISO / IEC N337: C ++ 11 (Saya hanya memiliki draft di tangan)
5.2.10 Menafsirkan kembali pemeran
Suatu pointer dapat secara eksplisit dikonversi ke tipe integral apa pun yang cukup besar untuk menahannya. Fungsi pemetaan ditentukan oleh implementasi. [Catatan: Ini dimaksudkan untuk tidak mengejutkan bagi mereka yang mengetahui struktur pengalamatan mesin yang mendasarinya. —Kirim catatan] Nilai tipe std :: nullptr_t dapat dikonversi ke tipe integral; konversi memiliki arti dan validitas yang sama dengan konversi (batal *) 0 ke tipe integral. [Catatan: Reinterpret_cast tidak dapat digunakan untuk mengonversi nilai tipe apa pun menjadi tipe std :: nullptr_t. —Kirim catatan]
Nilai tipe integral atau tipe enumerasi dapat secara eksplisit dikonversi ke pointer. Pointer yang dikonversi menjadi integer dengan ukuran yang cukup (jika ada pada implementasi) dan kembali ke tipe pointer yang sama akan memiliki nilai aslinya; pemetaan antara pointer dan integer jika tidak ditentukan implementasi. [Catatan: Kecuali seperti yang dijelaskan dalam 3.7.4.3, hasil konversi tersebut tidak akan menjadi nilai penunjuk yang diturunkan dengan aman. —Kirim catatan]
Seperti yang dapat Anda lihat di sini, dengan beberapa tahun lagi, C ++ menemukan bahwa aman untuk mengasumsikan bahwa pemetaan ke bilangan bulat ada, sehingga tidak ada lagi pembicaraan tentang perilaku yang tidak terdefinisi (meskipun ada kontradiksi yang menarik antara bagian 4 dan 5 dengan frasa "jika ada pada implementasi")
Sekarang apa yang harus Anda ambil dari ini?
Taruhan terbaik: casting ke (char *). Spesifikasi C dan C ++ penuh dengan aturan yang menentukan pengemasan array dan struct, dan keduanya selalu memungkinkan casting dari pointer apa saja ke char *. char selalu 1 byte (tidak dijamin dalam C, tetapi oleh C ++ 11 ia telah menjadi bagian dari bahasa yang diamanatkan, sehingga relatif aman untuk menganggapnya 1 byte di mana-mana). Hal ini memungkinkan Anda untuk melakukan aritmatika pointer pada level byte-by-byte tanpa menggunakan benar-benar perlu mengetahui implementasi representasi spesifik dari pointer.
sumber
char *
? Saya sedang memikirkan mesin hipotetis dengan ruang alamat terpisah untuk kode dan data.char
selalu 1 byte dalam C. Mengutip dari standar C: "Ukuran operator menghasilkan ukuran (dalam byte) dari operandnya" dan "Ketika sizeof diterapkan pada operan yang memiliki tipe char, unsigned char, atau char yang ditandatangani, (atau versi yang memenuhi syarat) hasilnya adalah 1. " Mungkin Anda berpikir bahwa byte adalah 8 bit. Itu belum tentu demikian. Agar sesuai dengan standar, byte harus mengandung setidaknya 8 bit.Pada sebagian besar arsitektur, jenis penunjuk tidak ada lagi setelah diterjemahkan ke dalam kode mesin (kecuali untuk "penunjuk gemuk"). Oleh karena itu, penunjuk ke suatu
int
tidak dapat dibedakan dari penunjuk kedouble
, setidaknya dengan sendirinya. *[*] Meskipun demikian, Anda masih dapat membuat tebakan berdasarkan jenis operasi yang Anda terapkan padanya.
sumber
Suatu hal penting untuk dipahami tentang C dan C ++ adalah tipe apa sebenarnya. Yang benar-benar mereka lakukan adalah menunjukkan kepada kompiler bagaimana menafsirkan sekumpulan bit / byte. Mari kita mulai dengan kode berikut:
Bergantung pada arsitektur, integer biasanya diberikan 32 bit ruang untuk menyimpan nilai itu. Itu berarti bahwa ruang dalam memori tempat var disimpan akan terlihat seperti "11111111 11111111 11111010 11000111" atau dalam hex "0xFFFFFAC7". Itu dia. Itu semua yang disimpan di lokasi itu. Semua tipe lakukan adalah memberi tahu kompiler bagaimana menafsirkan informasi itu. Pointer tidak berbeda. Jika saya melakukan sesuatu seperti ini:
Kemudian kompiler akan mendapatkan lokasi var, dan kemudian menyimpan alamat itu dengan cara yang sama dengan potongan kode pertama menyimpan nilai -1337. Tidak ada perbedaan dalam cara mereka disimpan, hanya dalam cara mereka digunakan. Bahkan tidak masalah bahwa saya membuat var_ptr pointer ke int. Jika Anda mau, Anda bisa melakukannya.
Ini akan menyalin nilai hex var (0xFFFFFAC7) di atas ke lokasi yang menyimpan nilai var2. Jika kita kemudian menggunakan var2, kita akan menemukan bahwa nilainya akan menjadi 4294965959. Bytes dalam var2 sama dengan var, tetapi nilai numeriknya berbeda. Compiler menafsirkannya secara berbeda karena kami memberi tahu bahwa bit-bit itu mewakili panjang yang tidak ditandai. Anda dapat melakukan hal yang sama untuk nilai pointer juga.
Anda akhirnya menafsirkan nilai yang mewakili alamat var sebagai int yang tidak ditandatangani dalam contoh ini.
Semoga ini menjelaskan hal-hal untuk Anda dan memberi Anda wawasan yang lebih baik tentang bagaimana C bekerja. Harap dicatat bahwa Anda TIDAK HARUS melakukan hal gila yang saya lakukan dalam dua baris di bawah ini dalam kode produksi aktual. Itu hanya untuk demonstrasi.
sumber
Bilangan bulat.
Ruang alamat di komputer diberi nomor secara berurutan, mulai dari 0, dan bertambah dengan 1. Jadi, penunjuk akan menyimpan angka integer yang sesuai dengan alamat di ruang alamat.
sumber
Jenis-jenis bergabung.
Secara khusus, tipe-tipe tertentu bergabung, hampir seolah-olah mereka diparameterisasi dengan placeholder. Tipe array dan pointer seperti ini; mereka memiliki satu placeholder seperti itu, yang merupakan jenis elemen array atau hal yang ditunjukkan, masing-masing. Jenis fungsi juga seperti ini; mereka dapat memiliki beberapa tempat penampung untuk parameter, dan tempat penampung untuk jenis pengembalian.
Variabel yang dinyatakan memiliki pointer ke char memiliki tipe "pointer to char". Variabel yang dinyatakan memiliki pointer ke pointer ke int memiliki tipe "pointer ke pointer ke int".
A (nilai) ketik "pointer to pointer to int" dapat diubah menjadi "pointer to int" oleh operasi dereference. Jadi, gagasan tipe bukan hanya kata-kata tetapi konstruk yang signifikan secara matematis, menentukan apa yang dapat kita lakukan dengan nilai-nilai tipe (seperti dereferensi, atau lulus sebagai parameter atau menetapkan ke variabel; itu juga menentukan ukuran (jumlah byte) dari operasi pengindeksan, aritmatika, dan kenaikan / penurunan).
NB. Jika Anda ingin mengetahui lebih jauh tentang tipe-tipe, coba blog ini: http://www.goodmath.org/blog/2015/05/13/expressions-and-arity-part-1/
sumber