Metoda opisana przez Bruce'a Eckela okazała się pomocna i łatwa do naśladowania:
Definiowanie wskaźnika funkcji
Aby zdefiniować wskaźnik do funkcji, która nie ma argumentów ani wartości zwracanej, mówisz:
void (*funcPtr)();
Kiedy patrzysz na taką złożoną definicję, najlepszym sposobem na atak jest rozpoczęcie od środka i wypracowanie wyjścia.„Rozpoczęcie w środku” oznacza rozpoczęcie od nazwy zmiennej, którą jest funcPtr. „Wypracowanie wyjścia” oznacza patrzenie w prawo na najbliższy przedmiot (w tym przypadku nic; prawy nawias nie pozwala się zatrzymać), a następnie patrzenie w lewo (wskaźnik oznaczony gwiazdką), a następnie patrzenie w prawo ( pusta lista argumentów wskazująca funkcję, która nie przyjmuje żadnych argumentów), a następnie patrząc w lewo (void, co oznacza, że funkcja nie ma wartości zwracanej). Ten ruch prawo-lewo-prawo działa z większością deklaracji.
Aby przejrzeć, „zacznij od środka” („funcPtr to ...”), idź w prawo (nic tam nie ma - prawy nawias zatrzyma cię), idź w lewo i znajdź „*” („ ... wskaźnik do ... ”), przejdź w prawo i znajdź pustą listę argumentów („ ... funkcja, która nie przyjmuje argumentów ... ”), przejdź w lewo i znajdź pustkę („ funcPtr jest wskaźnik do funkcji, która nie przyjmuje argumentów i zwraca void ”).
Możesz się zastanawiać, dlaczego * funcPtr wymaga nawiasów. Jeśli ich nie użyjesz, kompilator zobaczy:
void *funcPtr();
Będziesz deklarował funkcję (która zwraca void *) zamiast definiować zmienną. Możesz myśleć o kompilatorze jako przechodzącym przez ten sam proces, który robisz, kiedy odkryjesz, czym powinna być deklaracja lub definicja. Potrzebuje tych nawiasów, aby „zderzyć się z”, więc wraca w lewo i znajduje „*”, zamiast kontynuować w prawo i znajdować pustą listę argumentów.
Skomplikowane deklaracje i definicje
Nawiasem mówiąc, gdy zorientujesz się, jak działa składnia deklaracji C i C ++, możesz utworzyć znacznie bardziej skomplikowane elementy. Na przykład:
//: C03:ComplicatedDefinitions.cpp
/* 1. */ void * (*(*fp1)(int))[10];
/* 2. */ float (*(*fp2)(int,int,float))(int);
/* 3. */ typedef double (*(*(*fp3)())[10])();
fp3 a;
/* 4. */ int (*(*f4())[10])();
int main() {} ///:~
Przejdź każdy z nich i skorzystaj z prowadnicy po prawej i lewej stronie, aby to rozgryźć. Liczba 1 mówi „fp1 jest wskaźnikiem funkcji, która pobiera argument liczby całkowitej i zwraca wskaźnik do tablicy 10 pustych wskaźników”.
Liczba 2 mówi: „fp2 jest wskaźnikiem funkcji, która pobiera trzy argumenty (int, int i float) i zwraca wskaźnik do funkcji, która przyjmuje argument liczby całkowitej i zwraca liczbę zmiennoprzecinkową”.
Jeśli tworzysz wiele skomplikowanych definicji, możesz użyć typedef. Liczba 3 pokazuje, w jaki sposób typedef oszczędza wpisywanie skomplikowanego opisu za każdym razem. Mówi: „Fp3 to wskaźnik do funkcji, która nie przyjmuje argumentów i zwraca wskaźnik do tablicy 10 wskaźników do funkcji, które nie przyjmują argumentów i zwracają podwójną liczbę”. Potem mówi „a jest jednym z tych typów fp3”. Typedef jest ogólnie przydatny do tworzenia skomplikowanych opisów z prostych.
Liczba 4 jest deklaracją funkcji zamiast definicji zmiennej. Mówi: „f4 jest funkcją, która zwraca wskaźnik do tablicy 10 wskaźników do funkcji zwracających liczby całkowite”.
Rzadko będziesz potrzebować tak skomplikowanych deklaracji i definicji jak te. Jeśli jednak przejdziesz ćwiczenie polegające na ich rozszyfrowaniu, nie będziesz nawet lekko zaniepokojony tymi nieco skomplikowanymi, które możesz spotkać w prawdziwym życiu.