Ponieważ dopiero uczysz się C, radzę ci najpierw naprawdę spróbować zrozumieć różnice między tablicami i wskaźnikami, zamiast zwykłych rzeczy.
W obszarze parametrów i tablic istnieje kilka zagmatwanych reguł, które powinny być jasne, zanim przejdziemy dalej. Po pierwsze, to , co deklarujesz w liście parametrów, jest traktowane jako specjalne. Są takie sytuacje, w których rzeczy nie mają sensu jako parametr funkcji w C. Są to
- Działa jako parametry
- Tablice jako parametry
Tablice jako parametry
Druga może nie jest od razu jasna. Ale staje się jasne, gdy weźmiesz pod uwagę, że rozmiar wymiaru tablicy jest częścią typu w C (a tablica, której rozmiar wymiaru nie jest podana, ma niepełny typ). Tak więc, gdybyś utworzył funkcję, która pobiera tablicę według wartości (otrzymuje kopię), mogłaby to zrobić tylko dla jednego rozmiaru! Ponadto tablice mogą stać się duże, a C stara się działać tak szybko, jak to możliwe.
Z tych powodów w języku C wartości-tablic nie istnieją. Jeśli chcesz uzyskać wartość tablicy, zamiast tego otrzymasz wskaźnik do pierwszego elementu tej tablicy. I tak właściwie już jest rozwiązanie. Zamiast rysować z góry nieprawidłowy parametr tablicy, kompilator C przekształci typ odpowiedniego parametru, aby był wskaźnikiem. Pamiętaj o tym, to jest bardzo ważne. Parametr nie będzie tablicą, ale zamiast tego będzie wskaźnikiem do odpowiedniego typu elementu.
Teraz, jeśli spróbujesz przekazać tablicę, zamiast tego przekazywany jest wskaźnik do pierwszego elementu tablicy.
Wycieczka: Funkcje jako parametry
Na zakończenie, a ponieważ myślę, że pomoże ci to lepiej zrozumieć sprawę, przyjrzyjmy się, jaki jest stan rzeczy, gdy spróbujesz przyjąć funkcję jako parametr. Rzeczywiście, najpierw nie będzie to miało żadnego sensu. Jak parametr może być funkcją? Hm, oczywiście chcemy mieć zmienną w tym miejscu! Zatem kompilator ponownie przekształca funkcję we wskaźnik funkcji . Próba przekazania funkcji spowoduje zamiast tego przekazanie wskaźnika do odpowiedniej funkcji. Tak więc następujące są takie same (analogiczne do przykładu tablicy):
void f(void g(void));
void f(void (*g)(void));
Zwróć uwagę, że *g
potrzebne są nawiasy wokół . W przeciwnym razie określiłby funkcję powracającą void*
zamiast wskaźnika do funkcji powracającej void
.
Wróćmy do tablic
Teraz powiedziałem na początku, że tablice mogą mieć niekompletny typ - co dzieje się, jeśli nie podasz jeszcze rozmiaru. Ponieważ już ustaliliśmy, że parametr tablicy nie istnieje, ale zamiast tego każdy parametr tablicy jest wskaźnikiem, rozmiar tablicy nie ma znaczenia. Oznacza to, że kompilator przetłumaczy wszystkie poniższe elementy i wszystkie oznaczają to samo:
int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);
Oczywiście nie ma sensu umieszczać w nim dowolnego rozmiaru i jest po prostu wyrzucany. Z tego powodu C99 wymyślił nowe znaczenie tych liczb i pozwala na pojawienie się innych rzeczy w nawiasach:
// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory.
int main(int c, char *argv[static 5]);
// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);
// says the same as the previous one
int main(int c, char ** const argv);
Ostatnie dwie linie mówią, że nie będziesz w stanie zmienić "argv" w funkcji - stał się on wskaźnikiem do stałej. Jednak tylko kilka kompilatorów C obsługuje te funkcje C99. Ale te cechy jasno pokazują, że „tablica” w rzeczywistości nie jest jedną. To wskaźnik.
Słowo ostrzeżenia
Zauważ, że wszystko, co powiedziałem powyżej, jest prawdą tylko wtedy, gdy masz tablicę jako parametr funkcji. Jeśli pracujesz z tablicami lokalnymi, tablica nie będzie wskaźnikiem. Będzie zachowywał się jak wskaźnik, ponieważ jak wyjaśniono wcześniej, tablica zostanie przekonwertowana na wskaźnik po odczytaniu jej wartości. Nie należy go jednak mylić ze wskazówkami.
Oto jeden klasyczny przykład:
char c[10];
char **c = &c; // does not work.
typedef char array[10];
array *pc = &c; // *does* work.
// same without typedef. Parens needed, because [...] has
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;