Saya belajar sedikit C ++ dan saya bertarung dengan pointer. Saya mengerti bahwa saya dapat memiliki 3 level pointer dengan menyatakan:
int *(*x)[5];
jadi itu *x
adalah pointer ke array 5 elemen yang pointer ke int
. Saya juga tahu itu x[0] = *(x+0);
, x[1] = *(x+1)
dan seterusnya ....
Jadi, mengingat deklarasi di atas, mengapa x[0] != x[0][0] != x[0][0][0]
?
x[0]
,x[0][0]
danx[0][0][0]
memiliki berbagai jenis. Mereka tidak bisa dibandingkan. Apa maksudmu!=
?int **x[5]
adalah array dari 5 elemen. Sebuah elemen adalah pointer ke pointer ke int`int** x[5]
akan menjadi array dari lima pointer yang menunjuk ke pointer yang menunjuk ke int.int *(*x)[5]
adalah pointer ke array lima pointer yang mengarah ke int.x[0] != x[0][0] != x[0][0][0]
artinya? Ini bukan perbandingan yang valid di C ++. Bahkan jika Anda membaginya menjadix[0] != x[0][0]
danx[0][0] != x[0][0][0]
itu masih tidak valid. Jadi, apa maksud pertanyaan Anda?Jawaban:
x
adalah pointer ke array 5 pointerint
.x[0]
adalah array dari 5 pointer keint
.x[0][0]
adalah pointer ke sebuahint
.x[0][0][0]
adalah sebuahint
.Anda bisa melihatnya
x[0]
adalah array dan akan dikonversi menjadi penunjuk ke elemen pertama saat digunakan dalam ekspresi (dengan beberapa pengecualian). Oleh karena itux[0]
akan memberikan alamat elemen pertamanyax[0][0]
yaitu0x500
.x[0][0]
berisi alamatint
yang merupakan0x100
.x[0][0][0]
mengandungint
nilai10
.Jadi,
x[0]
sama dengan&x[0][0]
dan karenanya&x[0][0] != x[0][0]
,.Karenanya
x[0] != x[0][0] != x[0][0][0]
,.sumber
0x100
harus segera muncul di sebelah kiri kotak yang berisi10
, cara yang sama yang0x500
muncul di sebelah kiri kotaknya. Alih-alih itu jauh ke kiri, dan di bawah.menurut posting Anda sendiri,
yang disederhanakan
Kenapa harus sama?
Yang pertama adalah alamat beberapa pointer.
Yang kedua adalah alamat pointer lain.
Dan yang ketiga adalah beberapa
int
nilai.sumber
x[0][0]
adalah(x[0])[0]
, yaitu*((*(x+0))+0)
, tidak*(x+0+0)
. Dereferensi terjadi sebelum yang kedua[0]
.x[0][0] != *(x+0+0)
sama sepertix[2][3] != x[3][2]
.Berikut adalah tata letak memori dari pointer Anda:
x[0]
menghasilkan "address of array",x[0][0]
menghasilkan "pointer 0",x[0][0][0]
menghasilkan "some integer".Saya percaya, itu harus jelas sekarang, mengapa mereka semua berbeda.
Di atas cukup dekat untuk pemahaman dasar, itulah sebabnya saya menulisnya dengan cara saya menulisnya. Namun, seperti yang ditunjukkan oleh haccks dengan tepat, baris pertama tidak 100% tepat. Jadi, inilah semua detail bagusnya:
Dari definisi bahasa C, nilai
x[0]
adalah seluruh array pointer integer. Namun, array adalah sesuatu yang tidak bisa Anda lakukan dengan C. Anda selalu memanipulasi alamat atau elemen mereka, tidak pernah seluruh array secara keseluruhan:Anda dapat lolos
x[0]
kesizeof
operator. Tapi itu bukan penggunaan nilai, hasilnya tergantung dari jenisnya saja.Anda dapat mengambil alamatnya yang menghasilkan nilai
x
, yaitu "alamat array" dengan tipeint*(*)[5]
. Dengan kata lain:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x
Dalam semua konteks lain , nilai
x[0]
kehancuran akan menjadi pointer ke elemen pertama dalam array. Artinya, sebuah pointer dengan nilai "address of array" dan jenisnyaint**
. Efeknya sama seperti jika Anda menggunakan castx
typeint**
.Karena peluruhan array-pointer dalam kasus 3., semua penggunaan
x[0]
akhirnya menghasilkan pointer yang menunjukkan awal array pointer; panggilanprintf("%p", x[0])
akan mencetak isi sel memori yang dilabeli sebagai "alamat array".sumber
x[0]
bukan alamat array.x[0]
bukan alamat array, itu array itu sendiri. Saya telah menambahkan penjelasan mendalam tentang ini, dan mengapa saya menulis itux[0]
adalah "alamat array". Saya harap Anda menyukainya.printf("%zu\n", sizeof x[0]);
melaporkan ukuran array, bukan ukuran pointer.sizeof x[0]
...x[0]
dereferensi pointer terluar ( pointer ke array ukuran 5 dari pointer ke int) dan menghasilkan array ukuran 5 dari pointer keint
;x[0][0]
dereferensi pointer terluar dan indeks array, menghasilkan pointer keint
;x[0][0][0]
menyangkal segala sesuatu, menghasilkan nilai yang konkret.Ngomong-ngomong, jika Anda pernah merasa bingung dengan apa arti deklarasi ini, gunakan cdecl .
sumber
Biarkan mempertimbangkan langkah demi langkah ekspresi
x[0]
,x[0][0]
danx[0][0][0]
.Seperti
x
didefinisikan dengan cara berikutmaka ekspresi
x[0]
adalah array tipeint *[5]
. Mempertimbangkan bahwa ekspresix[0]
sama dengan ekspresi*x
. Itu adalah penereferensi pointer ke array kita mendapatkan array itu sendiri. Biarkan dilambangkan seperti kamu bahwa kita memiliki deklarasiEkspresi
x[0][0]
sama dengany[0]
dan memiliki tipeint *
. Biarkan dilambangkan seperti z yaitu kita memiliki deklarasiekspresi
x[0][0][0]
sama dengan ekspresiy[0][0]
yang pada gilirannya sama dengan ekspresiz[0]
dan memiliki tipeint
.Jadi kita punya
x[0]
memiliki tipeint *[5]
x[0][0]
memiliki tipeint *
x[0][0][0]
memiliki tipeint
Jadi mereka adalah objek dari tipe yang berbeda dan dengan cara ukuran yang berbeda.
Jalankan misalnya
sumber
Hal pertama yang harus saya katakan itu
Dari gambar berikut semuanya jelas.
Ini hanya sebuah contoh, di mana nilai x [0] [0] [0] = 10
dan alamat x [0] [0] [0] adalah 1001
alamat itu disimpan dalam x [0] [0] = 1001
dan alamat x [0] [0] adalah 2000
dan alamat itu disimpan di x [0] = 2000
Jadi x [0] [0] [0] ≠ x [0] [0] ≠ x [0]
.
EDITING
Program 1:
Keluaran
Program 2:
Keluaran
sumber
x[0]
tidak mengandung alamat semut. Ini sebuah array. Ini akan membusuk untuk menunjuk ke elemen pertama.Jika Anda melihat array dari perspektif dunia nyata, itu akan muncul sebagai berikut:
x[0]
adalah wadah pengiriman yang penuh dengan peti.x[0][0]
adalah peti tunggal, penuh kotak sepatu, dalam wadah pengiriman.x[0][0][0]
adalah kotak sepatu tunggal di dalam peti, di dalam wadah pengiriman.Sekalipun itu adalah satu-satunya kotak sepatu di satu-satunya peti dalam wadah pengiriman, itu masih kotak sepatu dan bukan wadah pengiriman
sumber
x[0][0]
akan ada satu peti penuh dengan selembar kertas yang memiliki lokasi kotak sepatu tertulis di atasnya?Ada prinsip dalam C ++ sehingga: deklarasi variabel menunjukkan dengan tepat cara menggunakan variabel. Pertimbangkan deklarasi Anda:
yang dapat ditulis ulang sebagai (untuk lebih jelas):
Karena prinsipnya, kami memiliki:
Karena itu:
Jadi Anda bisa mengetahui perbedaannya.
sumber
x[0]
adalah array 5 int, bukan pointer. (itu mungkin membusuk ke pointer di sebagian besar konteks tetapi perbedaannya penting di sini).*(*x)[5]
isint
, so(*x)[5]
isint *
, so*x
an(int *)[5]
, sox
an*((int *)[5])
. Artinya,x
adalah pointer ke pointer 5-array keint
.Anda mencoba membandingkan berbagai jenis berdasarkan nilai
Jika Anda mengambil alamat, Anda mungkin mendapatkan lebih dari yang Anda harapkan
Ingatlah bahwa deklarasi Anda membuat perbedaan
akan memungkinkan perbandingan Anda inginkan, karena
y
,y[0]
,y[0][0]
,y[0][0][0]
akan memiliki nilai yang berbeda dan jenis tetapi alamat yang samatidak menempati ruang yang berdekatan.
x
danx [0]
memiliki alamat yang sama, tapix[0][0]
danx[0][0][0]
masing-masing di alamat yang berbedasumber
int *(*x)[5]
berbeda denganint **x[5]
Menjadi
p
penunjuk: Anda menumpuk dereferensi denganp[0][0]
, yang setara dengan*((*(p+0))+0)
.Dalam referensi C (&) dan notasi (*):
Setara dengan:
Lihat itu, & * dapat di-refactored, cukup hapus:
sumber
p == p
.&(&p[0])[0]
berbeda denganp[0][0]
Jawaban yang lain benar, tetapi tidak satupun dari mereka yang menekankan gagasan bahwa ketiga hal itu mungkin mengandung nilai yang sama , dan karena itu mereka tidak lengkap.
Alasan ini tidak dapat dipahami dari jawaban lain adalah bahwa semua ilustrasi, meskipun bermanfaat dan jelas masuk akal dalam sebagian besar keadaan, gagal untuk menutupi situasi di mana pointer
x
menunjuk ke dirinya sendiri.Ini cukup mudah untuk dibangun, tetapi jelas agak sulit untuk dipahami. Dalam program di bawah ini, kita akan melihat bagaimana kita dapat memaksa ketiga nilai menjadi identik.
CATATAN: Perilaku dalam program ini tidak terdefinisi, tapi saya mempostingnya di sini murni sebagai demonstrasi menarik dari sesuatu yang dapat dilakukan pointer , tetapi tidak boleh .
Ini mengkompilasi tanpa peringatan di C89 dan C99, dan hasilnya adalah sebagai berikut:
Cukup menarik, ketiga nilai itu identik. Tapi ini seharusnya bukan kejutan! Pertama, mari kita uraikan programnya.
Kami mendeklarasikan
x
sebagai pointer ke array 5 elemen di mana setiap elemen bertipe pointer ke int. Deklarasi ini mengalokasikan 4 byte pada tumpukan runtime (atau lebih tergantung pada implementasi Anda; pada pointer mesin saya adalah 4 byte), jadix
merujuk ke lokasi memori yang sebenarnya. Dalam kelompok bahasa C, isinyax
hanya sampah, sesuatu yang tersisa dari penggunaan lokasi sebelumnya, jadix
itu sendiri tidak menunjuk ke mana pun — tentu saja tidak untuk mengalokasikan ruang.Jadi, secara alami, kita dapat mengambil alamat variabel
x
dan meletakkannya di suatu tempat, jadi itulah yang kita lakukan. Tapi kita akan pergi ke depan dan memasukkannya ke x sendiri Karena&x
memiliki tipe yang berbeda denganx
, kita perlu melakukan pemeran sehingga kita tidak mendapatkan peringatan.Model memori akan terlihat seperti ini:
Jadi blok 4 byte memori pada alamat
0xbfd9198c
berisi pola bit yang sesuai dengan nilai heksadesimal0xbfd9198c
. Cukup sederhana.Selanjutnya, kami mencetak tiga nilai. Jawaban yang lain menjelaskan apa yang dirujuk oleh masing-masing ungkapan, sehingga hubungannya harus jelas sekarang.
Kita dapat melihat bahwa nilainya sama, tetapi hanya dalam arti yang sangat rendah ... pola bit mereka identik, tetapi tipe data yang terkait dengan setiap ekspresi berarti nilainya yang ditafsirkan berbeda. Sebagai contoh, jika kita mencetak
x[0][0][0]
menggunakan string format%d
, kita akan mendapatkan angka negatif yang sangat besar, sehingga "nilai" dalam praktiknya berbeda, tetapi pola bitnya sama.Ini sebenarnya sangat sederhana ... dalam diagram, panah hanya menunjuk ke alamat memori yang sama daripada yang berbeda. Namun, sementara kami dapat memaksakan hasil yang diharapkan dari perilaku yang tidak terdefinisi, hanya saja - tidak terdefinisi. Ini bukan kode produksi tetapi hanya demonstrasi demi kelengkapan.
Dalam situasi yang masuk akal, Anda akan menggunakan
malloc
untuk membuat array 5 int pointer, dan lagi untuk membuat int yang diarahkan ke array itu.malloc
selalu mengembalikan alamat unik (kecuali Anda kehabisan memori, dalam hal ini mengembalikan NULL atau 0), jadi Anda tidak perlu khawatir tentang petunjuk referensial diri seperti ini.Semoga itulah jawaban lengkap yang Anda cari. Anda seharusnya tidak mengharapkan
x[0]
,,x[0][0]
danx[0][0][0]
menjadi sama, tetapi mereka bisa jika dipaksa. Jika ada yang terlintas di kepala Anda, beri tahu saya agar saya bisa mengklarifikasi!sumber
x[0]
tidak benar-benar mewakili objek yang valid dari jenis yang benarx
adalah pointer ke array, jadi kita bisa menggunakan[]
operator untuk menentukan offset dari pointer itu dan men-dereferensinya. Apa yang aneh di sana? Hasilnyax[0]
adalah array, dan C tidak mengeluh jika Anda mencetak menggunakan itu%p
karena begitulah penerapannya di bawahnya.-pedantic
flag tidak menghasilkan peringatan, jadi C tidak masalah dengan jenisnya ...Jenis
int *(*x)[5]
yaituint* (*)[5]
yaitu pointer ke array dari 5 pointer ke int.x
adalah alamat dari array pertama dari 5 pointer ke ints (alamat dengan tipeint* (*)[5]
)x[0]
alamat array pertama dari 5 pointer ke int (alamat yang sama dengan tipeint* [5]
) (alamat offset x dengan0*sizeof(int* [5])
indeks yaitu * ukuran-jenis-sedang-menunjuk-ke-dan-dereferensi)x[0][0]
adalah pointer pertama ke int dalam array (alamat yang sama dengan tipeint*
) (alamat offset x oleh0*sizeof(int* [5])
dan dereference dan kemudian oleh0*sizeof(int*)
dan dereference)x[0][0][0]
adalah int pertama yang ditunjukkan oleh pointer ke int (mengimbangi alamat x oleh0*sizeof(int* [5])
dan dereference dan mengimbangi alamat itu dengan0*sizeof(int*)
dan dereference dan mengimbangi alamat itu dengan0*sizeof(int)
dan dereference)Jenis
int *(*y)[5][5][5]
yaituint* (*)[5][5][5]
yaitu pointer ke array 3d dari 5x5x5 pointer ke intx
adalah alamat array 3d pertama dari pointer 5x5x5 ke ints dengan tipeint*(*)[5][5][5]
x[0]
adalah alamat array 3d pertama dari pointer 5x5x5 ke ints (alamat offset x oleh0*sizeof(int* [5][5][5])
dan dereference)x[0][0]
adalah alamat array 2d pertama dari pointer 5x5 ke ints (alamat offset x oleh0*sizeof(int* [5][5][5])
dan dereference lalu offset alamat itu dengan0*sizeof(int* [5][5])
)x[0][0][0]
adalah alamat dari array pertama dari 5 pointer ke ints (alamat offset x oleh0*sizeof(int* [5][5][5])
dan dereference dan mengimbangi alamat itu dengan0*sizeof(int* [5][5])
dan mengimbangi alamat itu dengan0*sizeof(int* [5])
)x[0][0][0][0]
adalah pointer pertama ke int dalam array (mengimbangi alamat x dengan0*sizeof(int* [5][5][5])
dan dereference dan mengimbangi alamat itu dengan0*sizeof(int* [5][5])
dan mengimbangi alamat itu dengan0*sizeof(int* [5])
dan mengimbangi alamat itu dengan0*sizeof(int*)
dan dereference)x[0][0][0][0][0]
adalah int pertama yang ditunjukkan oleh pointer ke int (mengimbangi alamat x oleh0*sizeof(int* [5][5][5])
dan dereference dan mengimbangi alamat itu dengan0*sizeof(int* [5][5])
dan mengimbangi alamat itu dengan0*sizeof(int* [5])
dan mengimbangi alamat itu dengan0*sizeof(int*)
dan dereference dan mengimbangi alamat itu dengan0*sizeof(int)
dan dereference)Adapun peluruhan array:
Ini sama dengan melewati
int* x[][5][5]
atauint* (*x)[5][5]
artinya mereka semua membusuk ke yang terakhir. Inilah sebabnya mengapa Anda tidak akan mendapatkan peringatan kompiler untuk digunakanx[6][0][0]
dalam fungsi tetapi Anda akan melakukannyax[0][6][0]
karena informasi ukuran itu dipertahankanx[0]
adalah alamat array 3d pertama dari pointer 5x5x5 ke intsx[0][0]
adalah alamat array 2d pertama dari pointer 5x5 ke intsx[0][0][0]
adalah alamat dari array pertama dari 5 pointer ke intx[0][0][0][0]
adalah pointer pertama ke int dalam arrayx[0][0][0][0][0]
adalah int pertama yang ditunjukkan oleh pointer ke intDalam contoh terakhir, secara semantik jauh lebih jelas untuk digunakan
*(*x)[0][0][0]
daripadax[0][0][0][0][0]
, ini karena yang pertama dan terakhir di[0]
sini ditafsirkan sebagai pointer dereference daripada indeks ke dalam array multidimensi, karena jenisnya. Namun mereka identik karena(*x) == x[0]
terlepas dari semantik. Anda juga dapat menggunakan*****x
, yang akan terlihat seperti Anda mendereferensi pointer 5 kali, tetapi sebenarnya ditafsirkan persis sama: offset, dereference, dereference, 2 offset menjadi array dan dereference, murni karena jenisnya Anda menerapkan operasi untuk.Pada dasarnya ketika Anda
[0]
atau*
suatu*
untuk jenis non array, itu adalah offset dan dereference karena urutan prioritas dari*(a + 0)
.Ketika Anda
[0]
atau*
seorang*
untuk sebuah array tipe maka itu offset maka dereference idempoten (dereference diselesaikan oleh compiler untuk menghasilkan alamat yang sama - itu operasi idempoten).Ketika Anda
[0]
atau*
tipe dengan tipe array 1d maka itu offset kemudian dereferensiJika Anda
[0]
atau**
tipe array 2d maka itu hanya offset yaitu offset dan kemudian dereference idempoten.Jika Anda
[0][0][0]
atau***
tipe array 3d maka itu dereference offset + idempoten kemudian dereferensi offset + idempoten kemudian dereferensi offset + idempoten kemudian dereferensi. Dereferensi sejati hanya terjadi ketika jenis array dilucuti sepenuhnya.Sebagai contoh dari
int* (*x)[1][2][3]
jenis membuka secara berurutan.x
memiliki tipeint* (*)[1][2][3]
*x
memiliki tipeint* [1][2][3]
(offset 0 + idempoten dereference)**x
memiliki tipeint* [2][3]
(offset 0 + idempoten dereference)***x
memiliki tipeint* [3]
(offset 0 + idempoten dereference)****x
memiliki tipeint*
(offset 0 + dereferensi)*****x
memiliki tipeint
(offset 0 + dereferensi)sumber