C pointer ke array / array dari disambiguasi pointer

463

Apa perbedaan antara deklarasi berikut:

int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);

Apa aturan umum untuk memahami deklarasi yang lebih kompleks?

George
sumber
54
Berikut ini adalah artikel yang bagus tentang membaca deklarasi kompleks di C: unixwiz.net/techtips/reading-cdecl.html
jesper
@ jesper Sayangnya, kualifikasi constdan volatilekualifikasi, yang penting dan sulit, tidak ada dalam artikel itu.
bukan pengguna
@ bukan-pengguna yang tidak relevan dengan pertanyaan ini. Komentar Anda tidak relevan. Tolong jangan.
user64742

Jawaban:

439
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers

Yang ketiga sama dengan yang pertama.

Aturan umum adalah prioritas operator . Itu bisa menjadi jauh lebih kompleks ketika fungsi pointer masuk ke dalam gambar.

Mehrdad Afshari
sumber
4
Jadi, untuk sistem 32 bit: int * arr [8]; / * 8x4 byte dialokasikan, untuk setiap pointer / int (* arr) [8]; / 4 byte dialokasikan, hanya pointer * /
George
10
Nggak. int * arr [8]: total alokasi 8x4 byte , 4 byte untuk setiap pointer. int (* arr) [8] benar, 4 byte.
Mehrdad Afshari
2
Saya harus membaca kembali apa yang saya tulis. Maksud saya 4 untuk setiap pointer. Terima kasih untuk bantuannya!
George
4
Alasan yang pertama sama dengan yang terakhir adalah karena selalu dibungkus dengan tanda kurung di sekitar deklarator. P [N] adalah deklarator array. P (....) adalah deklarator fungsi dan * P adalah deklarator pointer. Jadi semua yang berikut ini sama dengan tanpa tanda kurung (kecuali untuk salah satu fungsi '"()": int (((* p))); void ((g (void))); int * (a [1]); void (* (p ())).
Johannes Schaub - litb
2
Bagus dalam penjelasan Anda. Untuk referensi mendalam tentang presedensi dan asosiasi operator lihat halaman 53 Bahasa Pemrograman C (edisi kedua ANSI C) oleh Brian Kernighan dan Dennis Ritchie. Operator ( ) [ ] mengaitkan kiri ke kanan dan memiliki prioritas lebih tinggi daripada yang *dibaca int* arr[8]sebagai array ukuran 8 di mana setiap elemen menunjuk ke int dan int (*arr)[8]sebagai pointer ke array ukuran 8 yang menyimpan bilangan bulat
Mushy
267

Gunakan program cdecl , seperti yang disarankan oleh K&R.

$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>

Cara kerjanya juga sebaliknya.

cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
sigjuice
sumber
@ankii Sebagian besar distribusi Linux harus memiliki paket. Anda juga bisa membuat biner sendiri.
sigjuice
ah maaf karena tidak menyebutkan, macOS di sini. akan melihat apakah tersedia, jika situs web juga baik-baik saja. ^^ terima kasih telah memberi tahu saya tentang hal ini .. Jangan ragu untuk menandai NLN.
ankii
2
@ankii Anda dapat menginstal dari Homebrew (dan mungkin MacPorts?). Jika itu tidak sesuai dengan selera Anda, itu sepele untuk membangun Anda sendiri dari tautan Github di kanan atas cdecl.org (saya baru saja membangunnya di macOS Mojave). Kemudian cukup salin binari cdecl ke PATH Anda. Saya merekomendasikan $ PATH / bin, karena tidak perlu melibatkan root pada sesuatu yang sederhana seperti ini.
sigjuice
Oh belum membaca paragraf kecil tentang instalasi di readme. hanya beberapa perintah dan tanda untuk menangani dependensi .. Diinstal menggunakan minuman. :)
ankii
1
Ketika saya pertama kali membaca ini saya berpikir: "Saya tidak akan pernah turun ke tingkat ini". Hari berikutnya, saya mengunduhnya.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
126

Saya tidak tahu apakah itu memiliki nama resmi, tetapi saya menyebutnya Right-Left Thingy (TM).

Mulai dari variabel, lalu ke kanan, dan ke kiri, dan ke kanan ... dan seterusnya.

int* arr1[8];

arr1 adalah array 8 pointer ke integer.

int (*arr2)[8];

arr2 adalah pointer (tanda kurung blok kanan-kiri) ke array 8 bilangan bulat.

int *(arr3[8]);

arr3 adalah array 8 pointer ke integer.

Ini akan membantu Anda keluar dengan deklarasi yang kompleks.

GManNickG
sumber
19
Saya telah mendengarnya disebut dengan nama "Aturan Spiral", yang dapat ditemukan di sini .
empat
6
@InkBlend: Aturan spiral berbeda dari aturan kanan-kiri . Yang pertama gagal dalam kasus-kasus seperti int *a[][10]sementara yang kedua berhasil.
legends2k
1
@dogeen Saya pikir istilah itu ada hubungannya dengan Bjarne Stroustrup :)
Anirudh Ramanathan
1
Seperti yang dikatakan InkBlend dan legends2k, ini adalah Aturan Spiral yang lebih kompleks dan tidak berfungsi dalam semua kasus, jadi tidak ada alasan untuk menggunakannya.
kotlomoy
Jangan lupa kiri ke kanan asosiasi ( ) [ ]dan kanan ke kiri* &
Mushy
28
int *a[4]; // Array of 4 pointers to int

int (*a)[4]; //a is a pointer to an integer array of size 4

int (*a[8])[5]; //a is an array of pointers to integer array of size 5 
Sunil bn
sumber
Bukankah yang ke-3 seharusnya: a adalah array pointer ke integer array ukuran 8? Maksud saya masing-masing array integer akan berukuran 8 kan?
Rushil Paul
2
@Rushil: tidak, subskrip terakhir ( [5]) mewakili dimensi bagian dalam. Ini berarti itu (*a[8])adalah dimensi pertama, dan dengan demikian merupakan representasi luar array. Apa yang masing-masing elemen dalam a poin adalah array integer yang berbeda ukuran 5.
zeboidlund
Terima kasih untuk yang ketiga. Saya mencari cara menulis array pointer ke array.
Deqing
15

Jawaban untuk dua yang terakhir juga dapat dikurangkan dari aturan emas dalam C:

Deklarasi mengikuti penggunaan.

int (*arr2)[8];

Apa yang terjadi jika Anda melakukan dereferensi arr2? Anda mendapatkan array 8 bilangan bulat.

int *(arr3[8]);

Apa yang terjadi jika Anda mengambil elemen dari arr3 ? Anda mendapatkan pointer ke integer.

Ini juga membantu ketika berhadapan dengan pointer ke fungsi. Untuk mengambil contoh sigjuice:

float *(*x)(void )

Apa yang terjadi ketika Anda melakukan dereferensi x? Anda mendapatkan fungsi yang dapat Anda panggil tanpa argumen. Apa yang terjadi ketika Anda menyebutnya? Ini akan mengembalikan pointer kefloat .

Namun, prioritas operator selalu rumit. Namun, menggunakan tanda kurung sebenarnya juga bisa membingungkan karena deklarasi mengikuti penggunaan. Setidaknya, bagi saya, secara intuitif arr2terlihat seperti array 8 pointer ke ints, tetapi sebenarnya sebaliknya. Hanya perlu membiasakan diri. Cukup beralasan untuk selalu menambahkan komentar pada deklarasi ini, jika Anda bertanya kepada saya :)

edit: contoh

By the way, saya hanya menemukan situasi berikut: fungsi yang memiliki matriks statis dan yang menggunakan pointer aritmatika untuk melihat apakah pointer baris di luar batas. Contoh:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))

int *
put_off(const int newrow[2])
{
    static int mymatrix[3][2];
    static int (*rowp)[2] = mymatrix;
    int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);

    memcpy(rowp, newrow, sizeof(*rowp));
    rowp += 1;
    if (rowp == border) {
        rowp = mymatrix;
    }

    return *rowp;
}

int
main(int argc, char *argv[])
{
    int i = 0;
    int row[2] = {0, 1};
    int *rout;

    for (i = 0; i &lt; 6; i++) {
        row[0] = i;
        row[1] += i;
        rout = put_off(row);
        printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
    }

    return 0;
}

Keluaran:

0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]

Perhatikan bahwa nilai batas tidak pernah berubah, sehingga kompiler dapat mengoptimalkannya. Ini berbeda dari apa yang awalnya ingin Anda gunakan:const int (*border)[3] yang menyatakan perbatasan sebagai pointer ke array 3 bilangan bulat yang tidak akan mengubah nilai selama variabel ada. Namun, penunjuk itu dapat diarahkan ke array lain kapan saja. Kami menginginkan perilaku semacam itu untuk argumen, sebagai gantinya (karena fungsi ini tidak mengubah salah satu dari bilangan bulat itu). Deklarasi mengikuti penggunaan.

(ps: jangan ragu untuk meningkatkan sampel ini!)

Hraban Luyat
sumber
5
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
Byron Formwalt
sumber
3

Sebagai aturan praktis, operator unary yang tepat (seperti [], (), dll) preferensi pengambilalihan yang kiri. Jadi, int *(*ptr)()[];akan menjadi pointer yang menunjuk ke fungsi yang mengembalikan array pointer ke int (dapatkan operator yang tepat secepat mungkin setelah Anda keluar dari tanda kurung)

Luis Colorado
sumber
Itu benar, tetapi juga ilegal. Anda tidak dapat memiliki fungsi yang mengembalikan array. Saya mencoba dan mendapatkan ini: di error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];bawah GCC 8 dengan$ gcc -std=c11 -pedantic-errors test.c
Cacahuete Frito
1
Alasan kompiler untuk memberikan kesalahan itu adalah karena ia mengartikan fungsi sebagai mengembalikan array, sebagai interpretasi yang benar dari negara aturan diutamakan. Itu ilegal sebagai deklarasi, tetapi deklarasi hukum int *(*ptr)();memungkinkan ekspresi seperti p()[3](atau (*p)()[3]) untuk digunakan nanti.
Luis Colorado
Ok, jika saya memahaminya, Anda berbicara tentang membuat fungsi yang mengembalikan pointer ke elemen pertama array (bukan array itu sendiri), dan kemudian menggunakan fungsi itu seolah-olah mengembalikan array? Ide yang menarik. Saya akan mencobanya. int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }dan menyebutnya seperti ini: foo(arr)[4];yang seharusnya berisi arr[2][4], kan?
Cacahuete Frito
benar ... tapi kamu juga benar, dan deklarasi itu ilegal. :)
Luis Colorado
2

Saya pikir kita bisa menggunakan aturan sederhana ..

example int * (*ptr)()[];
start from ptr 

" ptradalah pointer untuk" pergi ke kanan .. itu ")" sekarang ke kiri a "(" keluar ke kanan "()" jadi "ke fungsi yang tidak memerlukan argumen" ke kiri "dan mengembalikan pointer" pergi kanan "ke larik" ke kiri "bilangan bulat"

simal
sumber
Saya akan meningkatkan sedikit: "ptr adalah nama yang mengacu pada" ke kanan ... itu ), sekarang ke kiri ... itu *"pointer ke" ke kanan ... itu ), sekarang ke kiri ... itu a (keluar, ke kanan ()jadi "ke fungsi yang tidak memerlukan argumen" ke kanan ... []"dan mengembalikan array" ke kanan ;, jadi ke kiri ... *"pointer ke" ke kiri ... int"bilangan bulat"
Cacahuete Frito
2

Inilah cara saya menafsirkannya:

int *something[n];

Catatan tentang presedensi: operator subscript array ( []) memiliki prioritas lebih tinggi daripada operator dereferensi ( *).

Jadi, di sini kita akan menerapkan []sebelumnya *, membuat pernyataan setara dengan:

int *(something[i]);

Catat bagaimana deklarasi itu masuk akal: int nummeans numis aint , int *ptratau int (*ptr)means, (value at ptr) adalah int, yang membuat ptrpointer ke int.

Ini dapat dibaca sebagai, (nilai dari (nilai pada indeks ith dari sesuatu)) adalah bilangan bulat. Jadi, (nilai pada indeks ith dari sesuatu) adalah (integer pointer), yang menjadikan sesuatu array dari pointer integer.

Yang kedua,

int (*something)[n];

Agar masuk akal dari pernyataan ini, Anda harus terbiasa dengan fakta ini:

Catatan tentang representasi pointer array: somethingElse[i]sama dengan*(somethingElse + i)

Jadi, ganti somethingElsedengan (*something), kita dapatkan *(*something + i), yang merupakan bilangan bulat sesuai deklarasi. Jadi, (*something)beri kami sebuah array, yang membuat sesuatu setara (pointer ke array) .

nishantbhardwaj2002
sumber
0

Saya kira deklarasi kedua membingungkan banyak orang. Berikut cara mudah untuk memahaminya.

Mari kita memiliki array bilangan bulat, yaitu int B[8].

Mari kita juga memiliki variabel A yang menunjuk ke B. Sekarang, nilai pada A adalah B, yaitu (*A) == B. Karenanya A menunjuk ke array bilangan bulat. Dalam pertanyaan Anda, arr mirip dengan A.

Demikian pula dalam int* (*C) [8] , C adalah pointer ke array pointer ke integer.

nishantbhardwaj2002
sumber
0
int *arr1[5]

Dalam deklarasi ini, arr1adalah array dari 5 pointer ke integer. Alasan: Kurung kotak lebih diutamakan daripada * (operator dereferncing). Dan dalam tipe ini, jumlah baris diperbaiki (5 di sini), tetapi jumlah kolom adalah variabel.

int (*arr2)[5]

Dalam deklarasi ini, arr2adalah pointer ke array integer dari 5 elemen. Alasan: Di sini, () kurung memiliki prioritas lebih tinggi dari []. Dan dalam tipe ini, jumlah baris adalah variabel, tetapi jumlah kolom adalah tetap (5 di sini).

Krishna Kanth Yenumula
sumber
-7

Dalam pointer ke integer jika pointer bertambah maka ia pergi integer berikutnya.

dalam array pointer jika pointer bertambah itu melompat ke array berikutnya

Nikhil
sumber
" Dalam array pointer jika pointer bertambah itu melompat ke array berikutnya " ini jelas salah.
alk