Apa perbedaan antara deklarasi berikut:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Apa aturan umum untuk memahami deklarasi yang lebih kompleks?
const
dan volatile
kualifikasi, yang penting dan sulit, tidak ada dalam artikel itu.
Apa perbedaan antara deklarasi berikut:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Apa aturan umum untuk memahami deklarasi yang lebih kompleks?
const
dan volatile
kualifikasi, yang penting dan sulit, tidak ada dalam artikel itu.
Jawaban:
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.
( ) [ ]
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
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 )
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.
int *a[][10]
sementara yang kedua berhasil.
( ) [ ]
dan kanan ke kiri* &
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
[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.
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 arr2
terlihat 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 < 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!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
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)
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
int *(*ptr)();
memungkinkan ekspresi seperti p()[3]
(atau (*p)()[3]
) untuk digunakan nanti.
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?
Saya pikir kita bisa menggunakan aturan sederhana ..
example int * (*ptr)()[];
start from ptr
" ptr
adalah 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"
)
, 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"
Berikut adalah situs web yang menarik yang menjelaskan cara membaca tipe kompleks di C: http://www.unixwiz.net/techtips/reading-cdecl.html
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 num
meansnum
is aint
,int *ptr
atauint (*ptr)
means, (value atptr
) adalahint
, yang membuatptr
pointer keint
.
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 somethingElse
dengan (*something)
, kita dapatkan *(*something + i)
, yang merupakan bilangan bulat sesuai deklarasi. Jadi, (*something)
beri kami sebuah array, yang membuat sesuatu setara (pointer ke array) .
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.
int *arr1[5]
Dalam deklarasi ini, arr1
adalah 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, arr2
adalah 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).