Jaka jest różnica między następującymi deklaracjami:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Jaka jest ogólna zasada rozumienia bardziej złożonych deklaracji?
Jaka jest różnica między następującymi deklaracjami:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Jaka jest ogólna zasada rozumienia bardziej złożonych deklaracji?
const
ivolatile
, które są ważne i trudne.Odpowiedzi:
Trzeci jest taki sam jak pierwszy.
Ogólną zasadą jest pierwszeństwo operatora . Może stać się jeszcze bardziej skomplikowane, gdy na ekranie pojawią się wskaźniki funkcji.
źródło
( ) [ ]
kojarzą od lewej do prawej i mają wyższy priorytet niż*
odczytaneint* arr[8]
jako tablica wielkości 8, gdzie każdy element wskazuje na liczbęint (*arr)[8]
całkowitą i jako wskaźnik na tablicę wielkości 8, która zawiera liczby całkowiteUżyj programu cdecl , zgodnie z sugestią K&R.
Działa to również w drugą stronę.
źródło
Nie wiem, czy ma oficjalną nazwę, ale nazywam ją Prawicowo-Lewą Rzeczą (TM).
Zacznij od zmiennej, następnie idź w prawo, w lewo i w prawo ... i tak dalej.
arr1
to tablica 8 wskaźników do liczb całkowitych.arr2
jest wskaźnikiem (nawias blokuje prawy-lewy) do tablicy 8 liczb całkowitych.arr3
to tablica 8 wskaźników do liczb całkowitych.To powinno ci pomóc w przypadku złożonych deklaracji.
źródło
int *a[][10]
druga.( ) [ ]
o lewostronnym połączeniu z prawym i lewym od* &
źródło
[5]
) reprezentuje wymiar wewnętrzny. Oznacza to, że(*a[8])
jest to pierwszy wymiar, a zatem zewnętrzna reprezentacja tablicy. To, na coa
wskazuje każdy element, to inna tablica liczb całkowitych o rozmiarze 5.Odpowiedź na dwa ostatnie pytania można również odjąć od złotej reguły w C:
int (*arr2)[8];
Co się stanie, jeśli będziesz się obawiać
arr2
? Otrzymasz tablicę 8 liczb całkowitych.int *(arr3[8]);
Co się stanie, jeśli weźmiesz element
arr3
? Otrzymasz wskaźnik do liczby całkowitej.Pomaga to również w przypadku wskaźników do funkcji. Weźmy przykład sigjuice:
float *(*x)(void )
Co się stanie, gdy się wyreżyserujesz
x
? Otrzymujesz funkcję, którą możesz wywołać bez argumentów. Co się stanie, gdy go nazwiesz? Zwróci wskaźnik dofloat
.Jednak pierwszeństwo operatorów jest zawsze trudne. Jednak użycie nawiasów może być mylące, ponieważ deklaracja następuje po użyciu. Przynajmniej dla mnie intuicyjnie
arr2
wygląda jak tablica 8 wskaźników do liczb całkowitych, ale tak naprawdę jest odwrotnie. Wystarczy trochę przyzwyczaić się. Wystarczający powód, aby zawsze dodawać komentarz do tych deklaracji, jeśli mnie o to poprosisz :)edycja: przykład
Nawiasem mówiąc, natknąłem się na następującą sytuację: funkcję, która ma macierz statyczną i używa arytmetyki wskaźnika, aby sprawdzić, czy wskaźnik wiersza jest poza zakresem. Przykład:
Wynik:
Zauważ, że wartość granicy nigdy się nie zmienia, więc kompilator może ją zoptymalizować. Różni się to od tego, czego początkowo możesz chcieć użyć::
const int (*border)[3]
deklaruje obramowanie jako wskaźnik do tablicy 3 liczb całkowitych, które nie będą zmieniać wartości tak długo, jak długo istnieje zmienna. Jednak wskaźnik ten może być w dowolnym momencie skierowany na dowolną inną tablicę. Zamiast tego chcemy tego rodzaju zachowanie argumentu (ponieważ ta funkcja nie zmienia żadnej z tych liczb całkowitych). Deklaracja następuje po użyciu.(ps: możesz poprawić tę próbkę!)
źródło
źródło
Jako zasada, prawda operatorów jednoargumentowych (jak
[]
,()
itp) preferencji przejąć lewych. Tak,int *(*ptr)()[];
będzie to wskaźnik, który wskazuje na funkcję, która zwraca tablicę wskaźników do int (Get Right operatorów tak szybko, jak to możliwe, jak wydostać się z nawiasu)źródło
error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];
pod GCC 8 z$ gcc -std=c11 -pedantic-errors test.c
int *(*ptr)();
pozwala na użycie wyrażenia typup()[3]
(lub(*p)()[3]
) później.int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }
i nazwij to tak:foo(arr)[4];
co powinno zawieraćarr[2][4]
, prawda?Myślę, że możemy zastosować prostą zasadę ...
„
ptr
jest wskaźnikiem„ idź w prawo .. jego ”)„ teraz idź w lewo to a ”(„ wyjdź idź w prawo ”()„ więc ”do funkcji, która nie przyjmuje argumentów„ idź w lewo ”i zwraca wskaźnik„ idź prawo do tablicy „idź w lewo” liczb całkowitychźródło
)
, teraz idź w lewo ... to*
„wskaźnik do” idź w prawo ... to)
, teraz idź w lewo ... to(
wyjdzie, idź w prawo()
tak „do funkcji, która nie wymaga żadnych argumentów” idź w prawo ...[]
„i zwraca tablicę” idź w prawo;
końca, więc idź w lewo ...*
„wskazówek” iść w lewo ...int
„całkowite”Oto interesująca strona internetowa, która wyjaśnia, jak czytać złożone typy w C: http://www.unixwiz.net/techtips/reading-cdecl.html
źródło
Oto jak to interpretuję:
Tak więc tutaj zastosujemy
[]
wcześniej*
, dzięki czemu oświadczenie będzie równoważne z:Można to odczytać jako (wartość (wartość w indeksie czegoś)) jest liczbą całkowitą. Tak więc (wartość przy i-tym indeksie czegoś) jest (wskaźnikiem liczb całkowitych), co czyni z tego tablicę wskaźników liczb całkowitych.
W drugim
Aby zrozumieć to oświadczenie, musisz znać ten fakt:
Tak, zastępując
somethingElse
z(*something)
, otrzymujemy*(*something + i)
, który jest liczbą całkowitą, jak na deklaracji. Tak,(*something)
dał nam tablicę, która sprawia, że coś odpowiednik (wskaźnik do tablicy) .źródło
Wydaje mi się, że druga deklaracja jest dla wielu myląca. Oto prosty sposób, aby to zrozumieć.
Pozwala mieć tablicę liczb całkowitych, tj
int B[8]
.Miejmy także zmienną A, która wskazuje na B. Teraz wartość w A to B, tj
(*A) == B
. Stąd A wskazuje na tablicę liczb całkowitych. W twoim pytaniu arr jest podobny do A.Podobnie, w
int* (*C) [8]
, C jest wskaźnikiem do tablicy wskaźników na liczbę całkowitą.źródło
W tej deklaracji
arr1
jest tablica 5 wskaźników do liczb całkowitych. Powód: nawiasy kwadratowe mają wyższy priorytet niż * (operator wyrzeczenia). W tym typie liczba wierszy jest stała (tutaj 5), ale liczba kolumn jest zmienna.W tej deklaracji
arr2
jest wskaźnikiem do liczby całkowitej zawierającej 5 elementów. Powód: tutaj nawiasy kwadratowe mają wyższy priorytet niż []. W tym typie liczba wierszy jest zmienna, ale liczba kolumn jest stała (tutaj 5).źródło
We wskaźniku do liczby całkowitej, jeśli wskaźnik jest zwiększany, to przechodzi do następnej liczby całkowitej.
w tablicy wskaźnika, jeśli wskaźnik jest zwiększany, przeskakuje do następnej tablicy
źródło