Aşağıdaki bildirimler arasındaki fark nedir:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Daha karmaşık beyanları anlamak için genel kural nedir?
const
ve volatile
önemli ve zor hem elemeleri, o makalede eksik.
Aşağıdaki bildirimler arasındaki fark nedir:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Daha karmaşık beyanları anlamak için genel kural nedir?
const
ve volatile
önemli ve zor hem elemeleri, o makalede eksik.
Yanıtlar:
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
Üçüncüsü birincisi ile aynıdır.
Genel kural operatör önceliğidir . İşlev işaretçileri resme geldikçe daha da karmaşıklaşabilir.
( ) [ ]
soldan sağa ve daha yüksek önceliğe sahip ilişkilendirir *
çok oku int* arr[8]
boyutu 8, bir dizi olarak burada bir int ve her bir öğe noktaları int (*arr)[8]
tamsayılar tutan boyutu 8 oluşan bir dizi için bir işaretçi olarak
K&R'nin önerdiği şekilde cdecl programını kullanın .
$ 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>
Başka şekilde de çalışır.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
Resmi bir isme sahip olup olmadığını bilmiyorum, ama ona Sağ-Sol Thingy (TM) diyorum.
Değişkenle başlayın, sonra sağa, sola ve sağa gidin ... vb.
int* arr1[8];
arr1
tamsayılara 8 işaretli bir dizidir.
int (*arr2)[8];
arr2
8 tamsayıdan oluşan bir diziye bir işaretçi (parantez bloğu sağ-sol).
int *(arr3[8]);
arr3
tamsayılara 8 işaretli bir dizidir.
Bu, karmaşık bildirimlerde size yardımcı olacaktır.
int *a[][10]
olur.
( ) [ ]
Sol ve sağ sol sol tarafa sol unutmayın* &
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]
) iç boyutu temsil eder. Bu (*a[8])
, ilk boyut olduğu ve dolayısıyla dizinin dış temsili olduğu anlamına gelir . Her bir öğenin a
işaret ettiği nokta, 5 büyüklüğünde farklı bir tamsayı dizisidir.
Son ikisinin cevabı C'deki altın kuraldan da çıkarılabilir:
Beyanname kullanımı takip eder.
int (*arr2)[8];
Kayıttan çıkarılırsanız ne olur arr2
? 8 tamsayı bir dizi alırsınız.
int *(arr3[8]);
Bir elemanı alırsanız ne olur arr3
? Bir tamsayıya bir işaretçi alırsınız.
Bu aynı zamanda işlevlere işaretçilerle uğraşırken de yardımcı olur. Sigjuice örneğini ele almak için:
float *(*x)(void )
Vazgeçtiğinizde ne olur x
? Bağımsız değişken olmadan arayabileceğiniz bir işlev elde edersiniz. Ne zaman diyorsun? İşaretçiyi a işaretine döndürür float
.
Operatör önceliği her zaman zor. Ancak, bildirim kullanımdan sonra parantez kullanmak aslında kafa karıştırıcı olabilir. En azından, bana göre, sezgisel arr2
olarak 8 işaretçi bir dizi gibi görünüyor, ama aslında tam tersi. Sadece alışmak biraz zaman alır. Bana sorarsanız, bu bildirimlere her zaman bir yorum eklemek için yeterli neden :)
düzenle: örnek
Bu arada, sadece aşağıdaki duruma rastladım: statik bir matrisi olan ve satır işaretçisinin sınırların dışında olup olmadığını görmek için işaretçi aritmetiği kullanan bir işlev. Misal:
#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;
}
Çıktı:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
Sınır değerinin asla değişmediğini unutmayın, böylece derleyici bunu optimize edebilir. Bu, başlangıçta kullanmak isteyebileceğinizden farklıdır: const int (*border)[3]
değişken, var olduğu sürece değeri değişmeyecek 3 tamsayıdan oluşan bir diziye işaretçi olarak bildirir. Bununla birlikte, bu işaretçi herhangi bir zamanda bu tür başka bir diziye yönlendirilebilir. Bunun yerine argüman için bu tür davranışlar istiyoruz (çünkü bu işlev bu tamsayıların hiçbirini değiştirmez). Beyanname kullanımı takip eder.
(ps: bu örneği geliştirmek için çekinmeyin!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
Genel bir kural sağ birli operatörler (gibi gibi []
, ()
sol olanlar üzerinde, vs) önceliklidir. Bu nedenle, int *(*ptr)()[];
int'e bir işaretçi dizisi döndüren bir işleve işaret eden bir işaretçi olurdu (parantezten çıkarken mümkün olan en kısa sürede doğru işleçleri alın)
error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];
GCC 8 ile$ gcc -std=c11 -pedantic-errors test.c
int *(*ptr)();
gibi bir ifadenin kullanılmasına izin verir . p()[3]
(*p)()[3]
int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }
ve şöyle deyin: foo(arr)[4];
hangisini içermeli arr[2][4]
, değil mi?
Bence basit kuralı kullanabiliriz ..
example int * (*ptr)()[];
start from ptr
" ptr
sağa gitmek için bir işaretçi ..its") "şimdi sola git onun" ("dışarı çık sağa git" () "so" hiçbir argümanı almayan bir fonksiyona "sola git" ve bir işaretçi döndürür "git sağ "diziye" tamsayıların sola git "
)
, şimdi sola git ... bu *
" sağa git "için bir işaretçi ... o )
, şimdi sola git ... bir (
sağa git, çıkıp ()
... sağa git "hayır argümanlar alır bir işleve" öylesine []
"ve iadeler dizisi" doğru gitmek ;
ucunu, yani sola git ... *
"işaretçileri için" git sola ... int
"tamsayılar"
C'deki karmaşık türlerin nasıl okunacağını açıklayan ilginç bir web sitesi: http://www.unixwiz.net/techtips/reading-cdecl.html
İşte nasıl yorumlayacağım:
int *something[n];
Önceliğe ilişkin not: dizi alt simge operatörü (
[]
), dereference operatörüne (*
) göre daha yüksek önceliğe sahiptir .
Yani, burada daha []
önce uygulayacağız *
ve ifadeyi şuna eşit hale getireceğiz:
int *(something[i]);
Bir beyan duygusunu ilgili Not:
int num
aracınum
, bir olduğuint
,int *ptr
ya daint (*ptr)
anlamına gelir, (en değeriptr
) bir olduğuint
kılanptr
bir işaretçiint
.
Bu, (bir şeyin i indeksindeki değer) değeri bir tamsayı olarak okunabilir. Yani, (bir şeyin ith indeksindeki değer), bir şeyi tamsayı işaretçileri dizisi yapan bir (tamsayı işaretçisi) 'dir.
İkincisinde,
int (*something)[n];
Bu ifadeden bir anlam çıkarmak için bu gerçeğe aşina olmalısınız:
Dizinin seçilen gösterim ile Not:
somethingElse[i]
eşdeğerdir*(somethingElse + i)
Yani, yerine somethingElse
ile (*something)
, aldığımız *(*something + i)
bildiri uyarınca bir tamsayı olan. Yani, (*something)
bize bir dizi (bu bir diziye işaretçi) eşdeğer yapar bir dizi verildi .
Sanırım ikinci beyan birçok insan için kafa karıştırıcı. İşte bunu anlamanın kolay bir yolu.
Bir tamsayı dizisine sahip olalım, yani int B[8]
.
Ayrıca B'yi gösteren bir A değişkenine sahip olalım. Şimdi, A'daki değer B'dir, yani (*A) == B
. Bu nedenle A, bir tamsayı dizisini gösterir. Sorunuzda, arr A'ya benzer.
Benzer şekilde, içinde int* (*C) [8]
C, tamsayıya bir işaretçi dizisinin göstergesidir.
int *arr1[5]
Bu bildirimde, arr1
tamsayılara 5 işaretçi dizisidir. Sebep: Köşeli parantezlerin önceliği * (kayıt silme operatörü) üzerinde daha yüksektir. Ve bu tipte, satır sayısı sabittir (burada 5), ancak sütun sayısı değişkendir.
int (*arr2)[5]
Bu bildirimde, arr2
5 öğeden oluşan bir tamsayı dizisinin göstergesidir. Sebep: Burada, () köşeli ayraçlar [] 'den daha yüksek önceliğe sahiptir. Ve bu tipte, satır sayısı değişkendir, ancak sütun sayısı sabittir (burada 5).