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?
consti volatile, które są ważne i trudne.
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?
consti volatile, które są ważne i trudne.
Odpowiedzi:
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
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.
( ) [ ] kojarzą od lewej do prawej i mają wyższy priorytet niż *odczytane int* 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łkowite
Użyj programu cdecl , zgodnie z sugestią 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>
Działa to również w drugą stronę.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
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.
int* arr1[8];
arr1 to tablica 8 wskaźników do liczb całkowitych.
int (*arr2)[8];
arr2 jest wskaźnikiem (nawias blokuje prawy-lewy) do tablicy 8 liczb całkowitych.
int *(arr3[8]);
arr3 to tablica 8 wskaźników do liczb całkowitych.
To powinno ci pomóc w przypadku złożonych deklaracji.
int *a[][10]druga.
( ) [ ]o lewostronnym połączeniu z prawym i lewym od* &
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]) reprezentuje wymiar wewnętrzny. Oznacza to, że (*a[8])jest to pierwszy wymiar, a zatem zewnętrzna reprezentacja tablicy. To, na co a 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:
Deklaracja następuje po użyciu.
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 do float.
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 arr2wyglą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:
#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;
}
Wynik:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
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ę!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
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)
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 typu p()[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ę ...
example int * (*ptr)()[];
start from ptr
„ ptrjest 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
), 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
Oto jak to interpretuję:
int *something[n];
Uwaga na temat pierwszeństwa: operator indeksu tablicy (
[]) ma wyższy priorytet niż operator dereferencji (*).
Tak więc tutaj zastosujemy []wcześniej *, dzięki czemu oświadczenie będzie równoważne z:
int *(something[i]);
Uwaga na temat tego, w jaki sposób deklaracja ma sens:
int numoznaczanumtoint,int *ptrlubint (*ptr)oznacza, że (wartość atptr) jestint, co stanowiptrwskaźnik doint.
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
int (*something)[n];
Aby zrozumieć to oświadczenie, musisz znać ten fakt:
Uwaga na temat wskaźnika reprezentacji tablicy:
somethingElse[i]jest równoważna z*(somethingElse + i)
Tak, zastępując somethingElsez (*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) .
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ą.
int *arr1[5]
W tej deklaracji arr1jest 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.
int (*arr2)[5]
W tej deklaracji arr2jest 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).
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