Apakah nama array adalah penunjuk dalam C? Jika tidak, apa perbedaan antara nama array dan variabel pointer?
&array[0]
menghasilkan sebuah pointer, bukan sebuah array;)
Apakah nama array adalah penunjuk dalam C? Jika tidak, apa perbedaan antara nama array dan variabel pointer?
&array[0]
menghasilkan sebuah pointer, bukan sebuah array;)
Jawaban:
Array adalah sebuah array dan sebuah pointer adalah sebuah pointer, tetapi dalam banyak kasus array nama dikonversi menjadi pointer. Suatu istilah yang sering digunakan adalah bahwa mereka membusuk menjadi pointer.
Berikut ini sebuah array:
int a[7];
a
berisi ruang untuk tujuh bilangan bulat, dan Anda bisa memberi nilai pada salah satu dari mereka dengan tugas, seperti ini:
a[3] = 9;
Ini adalah sebuah pointer:
int *p;
p
tidak mengandung spasi untuk bilangan bulat, tetapi bisa menunjuk ke ruang untuk bilangan bulat. Kita dapat, misalnya, mengaturnya untuk menunjuk ke salah satu tempat dalam array a
, seperti yang pertama:
p = &a[0];
Yang bisa membingungkan adalah Anda juga bisa menulis ini:
p = a;
Ini tidak menyalin isi array a
ke pointer p
(apa pun artinya). Sebagai gantinya, nama array a
dikonversi menjadi pointer ke elemen pertamanya. Jadi tugas itu melakukan hal yang sama dengan yang sebelumnya.
Sekarang Anda dapat menggunakan p
cara yang mirip dengan array:
p[3] = 17;
Alasan bahwa ini bekerja adalah bahwa operator dereferencing array di C,, [ ]
didefinisikan dalam hal pointer. x[y]
berarti: mulai dengan pointer x
, maju y
elemen setelah pointer menunjuk, dan kemudian ambil apa pun yang ada. Menggunakan sintaks pointer aritmatika, x[y]
juga bisa ditulis sebagai *(x+y)
.
Agar ini bekerja dengan array normal, seperti kita a
, nama a
in a[3]
pertama harus dikonversi menjadi pointer (ke elemen pertama di a
). Kemudian kita melangkah 3 elemen ke depan, dan mengambil apa pun yang ada di sana. Dengan kata lain: ambil elemen pada posisi 3 dalam array. (Yang merupakan elemen keempat dalam array, karena yang pertama diberi nomor 0.)
Jadi, dalam ringkasan, nama array dalam program C (dalam kebanyakan kasus) dikonversi menjadi pointer. Satu pengecualian adalah ketika kita menggunakan sizeof
operator pada array. Jika a
dikonversi menjadi pointer dalam konteks ini, sizeof a
akan memberikan ukuran pointer dan bukan dari array yang sebenarnya, yang akan menjadi agak tidak berguna, jadi dalam hal ini a
berarti array itu sendiri.
functionpointer()
dan memiliki (*functionpointer)()
arti yang sama, anehnya.
sizeof()
, konteks lain di mana tidak ada array-> pembusukan pointer adalah operator &
- dalam contoh Anda di atas, &a
akan menjadi pointer ke array 7 int
, bukan pointer ke satu int
; yaitu, jenisnya akan int(*)[7]
, yang tidak secara implisit dapat dikonversi int*
. Dengan cara ini, fungsi sebenarnya dapat membawa pointer ke array dengan ukuran tertentu, dan menegakkan pembatasan melalui sistem tipe.
Saat array digunakan sebagai nilai, namanya mewakili alamat elemen pertama.
Ketika sebuah array tidak digunakan sebagai nilai, namanya mewakili seluruh array.
int arr[7];
/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */
/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */
Jika ekspresi tipe array (seperti nama array) muncul dalam ekspresi yang lebih besar dan itu bukan operan dari operator &
atau sizeof
, maka tipe ekspresi array dikonversi dari "N-element array of T" ke "pointer ke T", dan nilai dari ekspresi adalah alamat dari elemen pertama dalam array.
Singkatnya, nama array bukan pointer, tetapi dalam sebagian besar konteks diperlakukan seolah-olah itu adalah pointer.
Edit
Menjawab pertanyaan dalam komentar:
Jika saya menggunakan sizeof, apakah saya menghitung ukuran hanya elemen array? Kemudian array "head" juga memakan ruang dengan informasi tentang panjang dan pointer (dan ini berarti bahwa dibutuhkan lebih banyak ruang, daripada pointer normal akan)?
Saat Anda membuat array, satu-satunya ruang yang dialokasikan adalah ruang untuk elemen itu sendiri; tidak ada penyimpanan yang terwujud untuk penunjuk yang terpisah atau metadata apa pun. Diberikan
char a[10];
apa yang Anda dapatkan dalam memori
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[9]
+---+
The ekspresi a
mengacu pada seluruh array, tetapi tidak ada objek a
yang terpisah dari elemen array sendiri. Dengan demikian, sizeof a
memberi Anda ukuran (dalam byte) dari seluruh array. Ekspresi &a
memberi Anda alamat array, yang sama dengan alamat elemen pertama . Perbedaan antara &a
dan &a[0]
adalah jenis hasil 1 - char (*)[10]
dalam kasus pertama dan char *
yang kedua.
Di mana segala sesuatu menjadi aneh adalah ketika Anda ingin mengakses elemen individu - ekspresi a[i]
didefinisikan sebagai hasil dari *(a + i)
- diberi nilai alamat a
, i
elemen offset ( bukan byte ) dari alamat itu dan dereferensi hasilnya.
Masalahnya adalah itu a
bukan pointer atau alamat - itu adalah seluruh objek array. Jadi, aturan dalam C bahwa setiap kali kompiler melihat ekspresi tipe array (seperti a
, yang memiliki tipe char [10]
) dan ekspresi itu bukan operan dari operator sizeof
atau unary &
, tipe ekspresi itu dikonversi ("meluruh") ke tipe pointer ( char *
), dan nilai ekspresi adalah alamat elemen pertama array. Oleh karena itu, ekspresi a
memiliki tipe dan nilai yang sama dengan ekspresi &a[0]
(dan dengan ekstensi, ekspresi *a
memiliki tipe dan nilai yang sama dengan ekspresi a[0]
).
C berasal dari bahasa sebelumnya disebut B, dan B a
adalah sebuah objek pointer terpisah dari elemen array a[0]
, a[1]
, dll Ritchie ingin menjaga B Array semantik, tapi dia tidak ingin main-main dengan menyimpan objek pointer terpisah. Jadi dia menyingkirkannya. Sebagai gantinya, kompiler akan mengonversi ekspresi array ke ekspresi pointer selama terjemahan diperlukan.
Ingat bahwa saya mengatakan array tidak menyimpan metadata tentang ukurannya. Segera setelah ekspresi array "meluruh" ke sebuah pointer, yang Anda miliki hanyalah sebuah pointer ke satu elemen. Elemen itu mungkin yang pertama dari urutan elemen, atau mungkin objek tunggal. Tidak ada cara untuk mengetahui berdasarkan penunjuk itu sendiri.
Ketika Anda meneruskan ekspresi array ke suatu fungsi, semua fungsi yang diterima adalah sebuah pointer ke elemen pertama - ia tidak tahu seberapa besar array itu (inilah sebabnya gets
fungsi itu seperti ancaman dan akhirnya dihapus dari perpustakaan). Agar fungsi tahu berapa banyak elemen yang dimiliki array, Anda harus menggunakan nilai sentinel (seperti 0 terminator dalam string C) atau Anda harus meneruskan jumlah elemen sebagai parameter terpisah.
sizeof
adalah operator, dan ia mengevaluasi ke jumlah byte di operan (baik ekspresi yang menunjukkan objek, atau nama jenis dalam tanda kurung). Jadi, untuk sebuah array, sizeof
mengevaluasi jumlah elemen yang dikalikan dengan jumlah byte dalam satu elemen. Jika int
lebar 4 byte, maka array 5 elemen int
membutuhkan 20 byte.
[ ]
juga istimewa? Sebagai contoh,, int a[2][3];
maka untuk x = a[1][2];
, meskipun dapat ditulis ulang sebagai x = *( *(a+1) + 2 );
, di sini a
tidak dikonversikan ke tipe pointer int*
(meskipun jika a
argumen dari fungsi itu harus dikonversi ke int*
).
a
memiliki tipe int [2][3]
, yang "meluruh" untuk diketik int (*)[3]
. Ekspresi *(a + 1)
memiliki tipe int [3]
, yang "meluruh" int *
. Dengan demikian, *(*(a + 1) + 2)
akan ada tipe int
. a
menunjuk ke array 3-elemen pertama int
, a + 1
menunjuk ke array 3-elemen kedua int
, *(a + 1)
adalah array 3-elemen kedua int
, *(a + 1) + 2
menunjuk ke elemen ketiga dari array kedua int
, jadi *(*(a + 1) + 2)
adalah elemen ketiga dari array kedua int
. Bagaimana itu dipetakan ke kode mesin sepenuhnya tergantung pada kompiler.
Array dinyatakan seperti ini
int a[10];
mengalokasikan memori selama 10 int
detik. Anda tidak dapat memodifikasi a
tetapi Anda dapat melakukan aritmatika dengan pointer a
.
Pointer seperti ini mengalokasikan memori hanya untuk pointer p
:
int *p;
Itu tidak mengalokasikan apapun int
. Anda dapat memodifikasinya:
p = a;
dan gunakan subscript larik sebanyak mungkin dengan:
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
int
detik dengan durasi penyimpanan otomatis`.
Nama array dengan sendirinya menghasilkan lokasi memori, sehingga Anda dapat memperlakukan nama array seperti pointer:
int a[7];
a[0] = 1976;
a[1] = 1984;
printf("memory location of a: %p", a);
printf("value at memory location %p is %d", a, *a);
Dan hal-hal bagus lainnya yang dapat Anda lakukan untuk mengarahkan (mis. Menambahkan / mengurangi offset), Anda juga dapat melakukannya ke array:
printf("value at memory location %p is %d", a + 1, *(a + 1));
Dari segi bahasa, jika C tidak mengekspos array hanya sebagai semacam "pointer" (pedantically itu hanya lokasi memori. Tidak dapat menunjuk ke lokasi sewenang-wenang dalam memori, juga tidak dapat dikendalikan oleh programmer). Kami selalu perlu kode ini:
printf("value at memory location %p is %d", &a[1], a[1]);
Saya pikir contoh ini menjelaskan masalah ini:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
int **b = &a;
printf("a == &a: %d\n", a == b);
return 0;
}
Ini mengkompilasi dengan baik (dengan 2 peringatan) di gcc 4.9.2, dan mencetak yang berikut:
a == &a: 1
oops :-)
Jadi, kesimpulannya adalah tidak, array bukan sebuah pointer, itu tidak disimpan dalam memori (bahkan tidak hanya baca-satu) sebagai pointer, meskipun terlihat seperti itu, karena Anda dapat memperoleh alamatnya dengan operator & . Tapi - oops - operator itu tidak bekerja :-)), apa pun yang terjadi, Anda telah diperingatkan:
p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
int **b = &a;
^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
printf("a == &a: %d\n", a == b);
C ++ menolak setiap upaya seperti itu dengan kesalahan dalam waktu kompilasi.
Edit:
Inilah yang saya maksudkan untuk menunjukkan:
#include <stdio.h>
int main()
{
int a[3] = {9, 10, 11};
void *c = a;
void *b = &a;
void *d = &c;
printf("a == &a: %d\n", a == b);
printf("c == &c: %d\n", c == d);
return 0;
}
Meskipun c
dan a
"arahkan" ke memori yang sama, Anda bisa mendapatkan alamat c
penunjuk, tetapi Anda tidak bisa mendapatkan alamat a
penunjuk.
-std=c11 -pedantic-errors
, Anda mendapatkan kesalahan kompilator untuk menulis kode C yang tidak valid. Alasan mengapa adalah karena Anda mencoba untuk menetapkan int (*)[3]
variabel int**
, yang merupakan dua jenis yang sama sekali tidak ada hubungannya satu sama lain. Jadi apa yang seharusnya dibuktikan oleh contoh ini, saya tidak tahu.
int **
Tipe bukan itu intinya ada, kita harus lebih baik menggunakan void *
untuk ini.
Nama array berperilaku seperti penunjuk dan menunjuk ke elemen pertama array. Contoh:
int a[]={1,2,3};
printf("%p\n",a); //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0
Kedua pernyataan cetak akan memberikan hasil yang sama persis untuk mesin. Dalam sistem saya itu memberi:
0x7fff6fe40bc0
Array adalah kumpulan elemen sekuensial dan berdekatan dalam memori. Dalam C, nama array adalah indeks untuk elemen pertama, dan menerapkan offset, Anda dapat mengakses elemen lainnya. "Indeks ke elemen pertama" memang merupakan penunjuk ke arah memori.
Perbedaannya dengan variabel pointer adalah bahwa Anda tidak dapat mengubah lokasi yang ditunjuk oleh nama array, jadi mirip dengan pointer const (mirip, tidak sama. Lihat komentar Mark). Tetapi juga Anda tidak perlu melakukan dereferensi nama array untuk mendapatkan nilai jika Anda menggunakan pointer aritmetic:
char array = "hello wordl";
char* ptr = array;
char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'
Jadi jawabannya agak 'ya'.
Nama array adalah alamat elemen pertama array. Jadi ya nama array adalah pointer const.