Apakah nama array pointer?


203

Apakah nama array adalah penunjuk dalam C? Jika tidak, apa perbedaan antara nama array dan variabel pointer?


4
Tidak. Tetapi array sama & array [0]

36
@ pst: &array[0]menghasilkan sebuah pointer, bukan sebuah array;)
jalf

28
@Nava (dan pst): array dan & array [0] tidak benar-benar sama. Contoh kasus: sizeof (array) dan sizeof (& array [0]) memberikan hasil yang berbeda.
Thomas Padron-McCarthy

1
@ Thomas setuju, tetapi dalam hal pointer, ketika Anda dereference array dan & array [0], mereka menghasilkan nilai array yang sama [0] .ie * array == array [0]. Tidak ada yang bermaksud bahwa kedua pointer ini sama, tetapi dalam kasus khusus ini (menunjuk ke elemen pertama) Anda dapat menggunakan nama array.
Nava Carmon

1
Ini mungkin juga membantu dalam pemahaman Anda: stackoverflow.com/questions/381542 , stackoverflow.com/questions/660752
Dinah

Jawaban:


255

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;

ptidak 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 ake pointer p(apa pun artinya). Sebagai gantinya, nama array adikonversi menjadi pointer ke elemen pertamanya. Jadi tugas itu melakukan hal yang sama dengan yang sebelumnya.

Sekarang Anda dapat menggunakan pcara 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 yelemen 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 ain 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 sizeofoperator pada array. Jika adikonversi menjadi pointer dalam konteks ini, sizeof aakan memberikan ukuran pointer dan bukan dari array yang sebenarnya, yang akan menjadi agak tidak berguna, jadi dalam hal ini aberarti array itu sendiri.


5
Konversi otomatis serupa diterapkan pada fungsi pointer - keduanya functionpointer()dan memiliki (*functionpointer)()arti yang sama, anehnya.
Carl Norum

3
Dia tidak bertanya apakah array dan pointer sama, tetapi apakah nama array adalah pointer
Ricardo Amores

32
Nama array bukan pointer. Ini adalah pengidentifikasi untuk variabel tipe array, yang memiliki konversi implisit ke pointer tipe elemen.
Pavel Minaev

29
Selain itu, terlepas dari sizeof(), konteks lain di mana tidak ada array-> pembusukan pointer adalah operator &- dalam contoh Anda di atas, &aakan 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.
Pavel Minaev

3
@ onmyway133, periksa di sini untuk penjelasan singkat dan kutipan lebih lanjut.
Carl Norum

36

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 */

20

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 amemberi Anda ukuran (dalam byte) dari seluruh array. Ekspresi &amemberi Anda alamat array, yang sama dengan alamat elemen pertama . Perbedaan antara &adan &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, ielemen offset ( bukan byte ) dari alamat itu dan dereferensi hasilnya.

Masalahnya adalah itu abukan 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 sizeofatau 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 *amemiliki 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 getsfungsi 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.


  1. * Yang * dapat mempengaruhi bagaimana nilai alamat ditafsirkan - tergantung pada mesin.

Sudah lama mencari jawaban ini. Terima kasih! Dan jika Anda tahu, bisakah Anda memberi tahu sedikit lebih jauh apa itu ekspresi array. Jika saya menggunakan sizeof, apakah saya menghitung ukuran hanya elemen array? Kemudian array "head" juga membutuhkan ruang dengan informasi tentang panjang dan pointer (dan ini berarti bahwa dibutuhkan lebih banyak ruang, daripada pointer normal akan)?
Andriy Dmytruk

Dan satu hal lagi. Array dengan panjang 5 adalah tipe int [5]. Jadi itu dari mana kita tahu panjang ketika kita memanggil sizeof (array) - dari tipenya? Dan ini berarti bahwa array dengan panjang yang berbeda seperti jenis konstanta yang berbeda?
Andriy Dmytruk

@AndriyDmytruk: sizeofadalah operator, dan ia mengevaluasi ke jumlah byte di operan (baik ekspresi yang menunjukkan objek, atau nama jenis dalam tanda kurung). Jadi, untuk sebuah array, sizeofmengevaluasi jumlah elemen yang dikalikan dengan jumlah byte dalam satu elemen. Jika intlebar 4 byte, maka array 5 elemen intmembutuhkan 20 byte.
John Bode

Bukankah operator [ ]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 atidak dikonversikan ke tipe pointer int*(meskipun jika aargumen dari fungsi itu harus dikonversi ke int*).
Stan

2
@Stan: Ekspresi amemiliki 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. amenunjuk ke array 3-elemen pertama int, a + 1menunjuk ke array 3-elemen kedua int, *(a + 1) adalah array 3-elemen kedua int, *(a + 1) + 2menunjuk 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.
John Bode

5

Array dinyatakan seperti ini

int a[10];

mengalokasikan memori selama 10 intdetik. Anda tidak dapat memodifikasi atetapi 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

2
Array tidak selalu dialokasikan pada stack. Yhat adalah detail implementasi yang akan bervariasi dari kompiler ke kompiler. Dalam kebanyakan kasus, array statis atau global akan dialokasikan dari wilayah memori yang berbeda dari tumpukan. Susunan tipe const dapat dialokasikan dari wilayah memori lain
Mark Bessey

1
Saya pikir Grumdrig bermaksud mengatakan "mengalokasikan 10 intdetik dengan durasi penyimpanan otomatis`.
Lightness Races in Orbit

4

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]);

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 cdan a"arahkan" ke memori yang sama, Anda bisa mendapatkan alamat cpenunjuk, tetapi Anda tidak bisa mendapatkan alamat apenunjuk.


1
"Ini mengkompilasi dengan baik (dengan 2 peringatan)". Itu tidak baik. Jika Anda memberitahu gcc untuk mengkompilasinya sebagai standar C yang tepat dengan menambahkan -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.
Lundin

Lundin terima kasih atas komentar Anda. Anda tahu ada banyak standar. Saya mencoba mengklarifikasi apa yang saya maksud di edit. The int **Tipe bukan itu intinya ada, kita harus lebih baik menggunakan void *untuk ini.
Palo

-3

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

-4

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


1
Nama array tidak sama dengan pointer const. Diberikan: int a [10]; int * p = a; sizeof (p) dan sizeof (a) tidak sama.
Mark Bessey

1
Ada perbedaan lainnya. Secara umum, yang terbaik adalah tetap menggunakan terminologi yang digunakan oleh Standar C, yang secara khusus menyebutnya sebagai "konversi". Kutipan: "Kecuali ketika itu adalah operan dari sizeof operator atau unary & operator, atau string literal yang digunakan untuk menginisialisasi array, ekspresi yang memiliki tipe '' array of type '' dikonversi menjadi ekspresi dengan tipe ' 'pointer to type' 'yang menunjuk ke elemen awal objek array dan bukan nilai. Jika objek array memiliki kelas penyimpanan register, perilaku tidak terdefinisi. "
Pavel Minaev

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.